How to consume web service via GET methods? - c#

I have a normal 3rd party SOAP service with WSDL and stuff. The problem is - it only accepts GET requests. How can I access it in c#?
When I add that service to VS via Add Service Reference and try to use it as usual:
var service = new BaseSvcClient(
new BasicHttpContextBinding(),
new EndpointAddress("http://some.internal.ip/WebServices/Base.svc"));
var ver = service.Version();
I see (via fiddler) that it actually sends POST requests and web-service responds with Endpoint not found error message.
If I simply hit http://some.internal.ip/WebServices/Base.svc/Version in a browser the proper xml is returned.
I can use WebClient, but then I have to construct all the GET requests manually, which doesn't look good.
Are there other solutions?

I have found an answer that helped me a lot.
Basically if I take an autogenerated interface for the client, decorate methods with [WebGet] and use
var cf = new WebChannelFactory<IBaseSvc2>(new Uri("..."));
var service = cf.CreateChannel();
var result = service.Version();
it all works well. That's not a perfect solution, since changes won't be picked up automatically, so may be there are other solutions?
P.S. an interface for a web service is now like:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "BaseService.IBaseSvc")]
public interface IBaseSvc2
{
[System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IBaseSvc/Version", ReplyAction = "http://tempuri.org/IBaseSvc/VersionResponse")]
[WebGet]
VersionInformation Version();
}

You can achieve it by adding the protocols in config file
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>

Related

Rest + WCF Integration for Xamarin Forms

I am doing a Xamarin Forms project that requires connection to a WCF service. I must use Rest to access it, so I opted to use a PCL-compatible build of RestSharp. I have done many SOAP based web services, but this is my first dive into Rest, and I feel like I am missing something very basic. I have confirmed that my web service functions correctly when I make SOAP calls, so I believe I have set something up incorrectly.
Here is sample code for my web service:
Imports System.IO
Imports System.Net
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Web
<ServiceContract()>
Public Interface Iapi
<WebInvoke(Method:="PUT",
UriTemplate:="Login/Email/{Email}/Password/{Password}",
RequestFormat:=WebMessageFormat.Json,
ResponseFormat:=WebMessageFormat.Json)>
<OperationContract(AsyncPattern:=True)>
Function Login(email As String, password As String) As String
End Interface
Here is sample code for my attempt to call the service:
public void Login(string email, string password)
{
RestClient client = new RestClient("http://www.example.com/service.svc/");
RestRequest request = new RestRequest
{
Method = Method.PUT,
Resource = "Login/Email/{Email}/Password/{Password}",
RequestFormat = DataFormat.Json
};
request.AddParameter("Email", email, ParameterType.UrlSegment);
request.AddParameter("Password", password,ParameterType.UrlSegment);
client.ExecuteAsync(request, response => {
session = response.Content;
ActionCompleted(this, new System.EventArgs());
});
}
When I make the call above, I get no exceptions, just an empty string return value. The same thing happens in the browser. I suspect my service definition. I have several questions that are probably a bit basic, but I hope will also help other WCF/Rest beginners in the future.
1. What, if anything, is wrong with my UriTemplate in the definition of my service? What would a proper UriTemplate look like?
2. Should I be using the PUT method for this kind of service call, or is GET or POST more appropriate?
3. Is anything else obviously missing from my web service definition?
4. Am I correct in passing the full service uri (http://www.example.com/service.svc/) to the Rest client?
5. Any additional suggestions for a Rest beginner, specifically in relation to the WCF-Rest combination?
A proper URI-template could look something like this if you're using a GET:
C#
[OperationContract]
[WebGet(UriTemplate = "Book/{id}")]
Book GetBookById(string id);
VB:
<OperationContract()> _
<WebGet(UriTemplate:="Book/{id}")> _
Function GetBookById(ByVal id As String) As Book
Then you can call it using http://example.com/Book/1 for the book with ID==1.
In the Microsoft-world PUT is often used for creating or updating data, such as a new task, order etc. However, you CAN use it for login even if I personally think a POST or GET would be the more accurate approach. But that's just my oppinion.
See this question for more information:
PUT vs POST in REST
There's nothing that seems to be missing in you declaration.
If you cannot reach it with a browser, it's probably not the usage of RestSharp that's wrong. However, here's some notes. When using async methods you will often want to try using .NET's async/await-pattern. Then the request doesn't lock the main thread.
Example:
http://www.dosomethinghere.com/2014/08/23/vb-net-simpler-async-await-example/
Here's a little piece of code that I use in my Xamarin-projects for invoking services:
protected static async Task<T> ExecuteRequestAsync<T>(string resource,
HttpMethod method,
object body = null,
IEnumerable<Parameter> parameters = null) where T : new()
{
var client = new RestClient("http://example.com/rest/service.svc/");
var req = new RestRequest(resource, method);
AddRequestKeys(req);
if (body != null)
req.AddBody(body);
if (parameters != null)
{
foreach (var p in parameters)
{
req.AddParameter(p);
}
}
Func<Task<T>> result = async () =>
{
var response = await client.Execute<T>(req);
if (response.StatusCode == HttpStatusCode.Unauthorized)
throw new Exception(response.Data.ToString());
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception("Error");
return response.Data;
};
return await result();
}
Yes, that's correct.
How do you host your WCF? If using IIS, what does your web.config look like? Here's an example:
As a side note, I noticed you mentioned that you require access to a WCF-service. Have you considered using .NET Web API instead? It provides a more straight-forward approach for creating RESTful endpoints, without the need for configs. It's more simple to implement and to use, however it does not provide the same flexibility as a WCF-service does.
For debugging a WCF-service I strongly recommend "WCF Test client":
https://msdn.microsoft.com/en-us/library/bb552364(v=vs.110).aspx
Where can I find WcfTestClient.exe (part of Visual Studio)
With metadata-enabled in your web.config, you will be able to see all available methods. Example-configuration below:
<configuration>
<system.serviceModel>
<services>
<service name="Metadata.Example.SimpleService">
<endpoint address=""
binding="basicHttpBinding"
contract="Metadata.Example.ISimpleService" />
</service>
</services>
<behaviors>
</behaviors>
</system.serviceModel>
</configuration>
Source:
https://msdn.microsoft.com/en-us/library/ms734765(v=vs.110).aspx
If it doesn't help, could you provide your web.config and implementation of your service as well?

Get request URL when using ClientBase

I am using ClientBase in a proxy to call a REST over WCF service of mine using the service interface:
public class CommunicationServiceProxy : ClientBase<ICommunicationService>, ICommunicationService
I also add before each call a authorization header so my service will accept the request, like this:
private OperationContextScope AddHeader(OperationContextScope scope)
{
if (WebOperationContext.Current == null) return null;
WebOperationContext.Current.OutgoingRequest.Headers.Add("Authorization", Header);
return scope;
}
Everything is working fine, but now, because of security resons, I need to generate a signature of the request URL(eg. http://myservice/contact/getstatus?id=1), the problem is how to get this URL when using a proxy class like the one above, seens like WebOperationContext.Current.OutgoingRequest is missing this information.
Anyone have managed to get this? What are my options here?
Thanks
The solution I found is to use WCF extensions, more specificly IClientMessageInspector, there I have all the information I need to generate the signature.

How to make your Service-Reference proxy URL dynamic?

I have a web reference to web-service:
using (var client = new GetTemplateParamSoapClient("GetTemplateParamSoap"))
{
TemplateParamsKeyValue[] responsArray = client.GetTemplatesParamsPerId(
CtId, tempalteIds.ToArray());
foreach (var pair in responsArray)
{
string value = FetchTemplateValue(pair.Key, pair.Value);
TemplateComponentsData.Add(pair.Key, value);
}
}
Tried to change a web-reference url from c# code: as advice here:
1) http://www.codeproject.com/KB/XML/wsdldynamicurl.aspx
2) How to call a web service with a configurable URL
3) http://aspalliance.com/283_Setting_Web_Service_References_Dynamically
But I get symbol is missing when trying to do:
client.Url
In addition I couldn't find a property of "Url_behavior"
It sounds like you've already added the service reference, but here's a walkthrough on adding, updating and removing service references.
Once you've got one of those in your project, you can alter the endpoint URI with one of the constructor overloads, as John Saunders said above. To do this, you'll need to know the name of the endpoint in your config file. For instance, after you add your service you might have elements like this in your config file:
<endpoint address="http://bleh.com/services/servicename.asmx"
binding="basicHttpBinding" bindingConfiguration="ServiceNameSoap"
contract="ServiceReference1.ServiceNameSoap" name="ServiceNameSoap" />
Given that endpoint, you can change the address at runtime by using the following overload:
var proxy = new ServiceReference1.ServiceNameSoapClient("ServiceNameSoap",
"http://new-address.com/services/servicename.asmx");
You can also do it after construction, but that becomes a little bit harder. If you need to do so, see the documentation on the Endpoint property and the associated type ServiceEndpoint.

Creating a custom .svc handler for WCF service hosted in IIS

I am hosting a wcf service in IIS 7. I was curious if it would be possible to create my own .svc HttpHandler mapping and class to handle service requests.
For example, if I was to intercept any requests to files that ended with an extension of ".foo" I could add this to my web.config
<handlers>
<add name="*.foo_**" path="*.foo" verb="*" type="MyServer.FooHttpHandler" />
</handlers>
And I could have a class in my default root that did the following
public class FooHandler: IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// do stuff, validate?
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("filename={0}", url));
HttpContext.Current.Response.AddHeader("Content-Type", "fooMimeType");
HttpContext.Current.Response.WriteFile(url);
HttpContext.Current.Response.End();
}
public bool IsReusable
{
get { return false; }
}
}
Is it possible to do something like this with wcf .svc requests? I'm not sure if it'd be the exact same thing or not, since I'm not necessary serving a file to respond with, I want to intercept and proxy the response.
Or is a better way to implement a service behavior that does my required pre-service logic?
What are you trying to achieve? Not sure if you can replace the existing *.svc http handler - but what you can do much more easily is create your own custom ServiceHostFactory for the WCF service. You basically add one attribute your *.svc file:
<%# ServiceHost Language="C#" Debug="true"
Service="YourNamespace.YourService"
Factory="YourNamespace2.YourServiceHostFactory" %>
Using this, IIS will now instantiate your own YourServiceHostFactory and ask you to create an instance of the YourService class. Maybe you can hook into the flow here and do what you need to do?
You could use ASP.NET Routing and an IRouteHandler that returns your own IHttpHandler implementation as well if you really want to as well
http://msdn.microsoft.com/en-us/library/system.web.routing.iroutehandler.gethttphandler.aspx
http://msdn.microsoft.com/en-us/library/cc668201.aspx
I've done this in the past when setting up WCF Data Services and I don't see why it can't work here as well.

How to add SoapExtension attribute to generated web service client proxy without modifying the generated class?

I have created a SoapExtension class to capture the soap request and response from specific web service calls. In order to put this SoapExtension into effect, I have to add an attribute to the method in the generated proxy client.
For example, I've added the AuditSoapCapture attribute to this method:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://blahblah.com/webservices/AddressSearch", RequestNamespace = "http://blahblah.com/webservices/", ResponseNamespace = "http://blahblah.com/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[blahblah.TriadLite.Core.AuditSoapCapture]
public System.Data.DataSet AddressSearch(string HouseNumber, string StreetName, string ZipCode) {
object[] results = this.Invoke("AddressSearch", new object[] {
HouseNumber,
StreetName,
ZipCode});
return ((System.Data.DataSet)(results[0]));
}
I am looking for a way to add this attribute to specific methods without modifying the generated client proxy, as they will get lost when we regenerate. Can I do this in a another partial class or interface or some other way?
Thanks!
Unfortunately, you'll need to modify the proxy code. The other possibilities you mention will not work - a parial class will not overwrite existing functionality, and there is no way that I'm aware of getting an interface to do what you need (compounded by the fact that there is no way to even let the proxy generator know that you intend to implement an interface).
Something that I've done in the past, in a situation where you have access to the source of the webservice, is to write a little app that will parse the code (as text) in the .asmx.cs file of the webservice to extract the names of all the methods that are tagged with [WebMethod]. Then the app "fixes up" the References.cs by inserting appropriate attributes onto the proxied methods, based on some settings file or somesuch. This works well because the naming conventions in the proxy map very neatly to the method names in the original service.
I may just end up injecting my SoapExtension by putting it into the Web.config. This will cause it to be run on every WS call without a client proxy method attribute. Then, I will modify the SoapExtension to look up the called WS method name on a list, and if it is on the list, then do the rest of the SoapExtension logic. I figure the hit on the list in this small volume application isn't going to kill performance.
6 years ago this was posted... So not sure if this will help anyone at this point.
I ran into something similar with a call to an old SOAP web service that had a dynamically generated proxy class that we didn't want to modify as it was auto-generated from the wsdl by the project. In order to solve this problem here is what we did.
The proxy class generated by wsdl.exe is a partial class. We extended this class like so to add a property with the information we wanted to access in the soapextension. You can add as many properties as you want...
partial class mysoapwebservice
{
public string myproperty{ get; set; }
}
in the web.config we registered the soap extension globaly on the project
<webServices>
<soapExtensionTypes>
<add type="MySoapExtension" priority="1" group="Low"/>
</soapExtensionTypes>
</webServices>
In the code were we created the web service object 'mysoapwebservice' we set the value of the property we needed.
In the soapextension you can get a reference to the web service that was called as well as the values. You can also determine the method call.
`
public class MySoapExtension: SoapExtension
{
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
{
// web service client object
var webserviceobject= ((SoapClientMessage)message).Client;
// method from web service that was called
var calledMethod = (SoapClientMessage)message).MethodInfo;
// checked the client type of webserviceobject and
//added method / property specific logic here
}
}
}
// other soap extension code
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class CriptoExtensionAttribute : SoapExtensionAttribute
[CriptoExtension]
public partial class MainService{

Categories

Resources