Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebGet with No Parameters or UriTemplate Fails

Tags:

rest

c#

wcf

webget

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.

like image 782
Will Custode Avatar asked May 19 '15 16:05

Will Custode


2 Answers

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;
    }
}
like image 119
Will Custode Avatar answered Nov 05 '22 14:11

Will Custode


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.

like image 4
Brian from state farm Avatar answered Nov 05 '22 13:11

Brian from state farm