I have a RESTful WCF web service with the following API:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
MyResponseContract GetFileInfo();
When attempting to hit endpoint (using SOAPUI) I see the following error message:
The server encountered an error processing the request. Please see the service help page for constructing valid requests to the service.
I have SOAPUI set to hit it with a GET method call. When I switch it to a POST with no body, it fails with the following message:
Method not allowed.
This makes perfect sense: can't hit a GET with a POST. So I updated my code as follows:
[WebInvoke(ResponseFormat = WebMessageFormat.Json)]
MyResponseContract GetFileInfo();
And now I call it from SOAPUI with a POST method and it works. Curious. So I now change my code as follows:
[WebInvoke(ResponseFormat = WebMessageFormat.Json, Method = "GET")]
MyResponseContract GetFileInfo();
I've seen in a few posts that this is essentially equivalent to a WebGet
attribute. This also does not work.
So my question is: why doesn't this work as a WebGet
even though I am not accepting parameters or using a custom UriTemplate?
The Url I'm trying to hit it with (it's hosted locally in IIS) is:
http://localhost/Utilities/API/GetFileInfo
Update Given the comments below and the given answers, I am still faced with this problem. Some additional details.
My web-layer web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<customErrors mode="Off" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" maxReceivedMessageSize="10000000" />
</webHttpEndpoint>
</standardEndpoints>
<behaviors>
<endpointBehaviors>
<behavior name="exampleBehavior">
<callbackDebug includeExceptionDetailInFaults="true" />
<enableWebScript />
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="WebHttpBinding" maxReceivedMessageSize="10000000" />
</webHttpBinding>
</bindings>
<client>
<endpoint address="http://LOCALHOST/Utilities.AppService/API"
binding="webHttpBinding" bindingConfiguration="WebHttpBinding"
contract="Utilities.Common.API.IMyApi"
behaviorConfiguration="exampleBehavior" />
</client>
</system.serviceModel>
</configuration>
My app-layer web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<customErrors mode="Off" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" maxReceivedMessageSize="10000000" />
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</configuration>
My service interface
[ServiceContract(Namespace = "API")]
public interface IMyApi
{
[WebGet]
MyResponseContract GetFileInfo();
}
My web-layer implementation
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyApiWebService : ClientBase<IMyApi>, IMyApi
{
public MyResponseContract GetFileInfo()
{
return Channel.GetFileInfo();
}
}
My app-layer implementation
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyApiAppService : IMyApi
{
public MyResponseContract GetFileInfo()
{
return new MyResponseContract();
}
}
My web-layer Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("API", new WebServiceHostFactory(), typeof(MyApiWebService)));
}
My app-layer Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("API", new WebServiceHostFactory(), typeof(MyApiAppService)));
}
I'm not sure how much more detail I can provide. As you can see, given the solutions provided, I have implemented everything suggested to no avail. Whether I am trying to hit this WebGet
method by placing the web layer service url in the browser, or using SOAPUI, or trying to hit it with a C# unit test using a service client, I am unable to use WebGet
. Thanks again for all of your help.
And interesting note is that the App-layer URL works. But the web layer does not. So:
localhost/Utilities.AppService/API/GetFileInfo
works, whereas
localhost/Utilities.WebService/API/GetFileInfo
does not.
So this may not have been obvious until I updated recently, but I have two RESTful services that communicate with each other but live in separate domains. The Web-Layer service is the first point of contact and the App-Layer service is the actual work-doer. This being the case, I was able to debug a bit further and found that the actual exception was a 405 (Method Not Allowed) on the call from the Web to App layers. I found this link after much digging that solved my issue.
When using ClientBase<>
as you method of communication between services you essentially need to reestablish the operation context between calls. Otherwise everything becomes a POST and, as such, only POSTs work.
I hope this helps others, and I greatly appreciate everyone's help in debugging this.
To demonstrate what this looks like, here is what my updated, working web service implementation looks like:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyApiWebService : ClientBase<IMyApi>, IMyApi
{
public MyResponseContract GetFileInfo()
{
MyResponseContract output = null;
using(var context = new OperationContext(Channel as IContextChannel))
{
output = Channel.GetFileInfo();
}
return output;
}
}
The .NET WCF web service by default is set up to send text encoded SOAP messages. This means that the HTTP method is POST and there are required headers to tell the service what method to call.
I created a quick example using your service endpoint and here is the request generated from fiddler to talk to that endpoint.
POST http://localhost/Utilities/API/GetFileInfo/Service1.svc HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/IService1/GetFileInfo"
Host: localhost:8888
Content-Length: 136
Expect: 100-continue
Connection: Keep-Alive
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetFileInfo xmlns="http://tempuri.org/"/></s:Body></s:Envelope>
Getting back a response of
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 398
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcV29ya1xFSVAgV29ya1xRdWVzdGlvbnNcV0NGR2V0VGVzdFxTZXJ2aWNlMS5zdmM=?=
X-Powered-By: ASP.NET
Date: Thu, 21 May 2015 19:47:49 GMT
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetFileInfoResponse xmlns="http://tempuri.org/"><GetFileInfoResult xmlns:a="http://schemas.datacontract.org/2004/07/WCFGetTest" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:BoolValue>true</a:BoolValue><a:StringValue>MyResponseContract </a:StringValue></GetFileInfoResult></GetFileInfoResponse></s:Body></s:Envelope>
For you I think something in your SoapUI is not set correctly. Either the post data or the headers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With