Search code examples
c#wcfrestwebget

WebGet with No Parameters or UriTemplate Fails


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.


Solution

  • 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;
        }
    }