Click here to Skip to main content
15,886,362 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
I'm working on an established project that's based on WCF. It uses routing in order to expose a REST-ful API. Recently I had to change the behavior of one of the included services in order to return larger JSON-serialized responses without crashing. The change was trivial to make using a ServiceBehaviorAttribute (as shown when declaring the ServiceController class), but we wanted the solution to be configuration-based, not code-based.

NOTE: that attribute isn't actually present in the code. It's just there to illustrate how it would be used.
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Net;

namespace Company.Project.Service.Services.Interfaces
{
    [ServiceContract]
    public interface IServiceController
    {
        [OperationContract]
        [Description("Gets stuff")]
        [WebGet(UriTemplate = "/?stuffIds={stuffIds}", ResponseFormat = WebMessageFormat.Json)]
        IEnumerable<Stuff> GetStuff(string stuffIds);
    }
}

namespace Company.Project.Service.Services
{   
    [ServiceBehavior(MaxItemsInObjectGraph = 2147483647)]
    public class ServiceController : BaseController, IServiceController
    {
        public IEnumerable<Stuff> GetStuff(string stuffIds)
        {
            ///
        }
    }   
}

Here is (most of) the code that sets up routing:
C#
using System;
using System.ServiceModel.Activation;
using System.Web.Routing;

using Company.Project.Service.Services;

namespace Company.Project.Service.Host
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes();
        }

        private void RegisterRoutes()
        {
            ///

            RouteTable.Routes.Add(new ServiceRoute("service", new WebServiceHostFactory(), typeof(Services.ServiceController)));
        }
    }
}

The service configuration is as follows:
XML
<services>
  <service name="Company.Project.Service.Services.ServiceControllerZZTop"
       behaviorConfiguration="secureBehavior">

    <endpoint name="service"
              contract="Company.Project.Service.Services.Interfaces.IServiceController"
              binding="basicHttpBinding"
              bindingConfiguration="AsmxConfiguration"
              address="ws"
              />
  </service>
</services>

<bindings>
  <basicHttpBinding>
    <binding name="AsmxConfiguration"
             receiveTimeout="00:03:00"
             sendTimeout="00:03:00"
             maxReceivedMessageSize="2147483647"
             maxBufferPoolSize="2147483647">
      <security mode="None"></security>
      <readerQuotas maxDepth="2147483647"
                    maxStringContentLength="2147483647"
                    maxArrayLength="2147483647"
                    maxBytesPerRead="2147483647"
                    maxNameTableCharCount="2147483647" />
    </binding>

    <binding name="SecureBasic"
             receiveTimeout="00:03:00"
             sendTimeout="00:03:00"
             maxReceivedMessageSize="2147483647"
             maxBufferPoolSize="2147483647">
      <security mode="Transport">
        <transport clientCredentialType="None" />
      </security>
      <readerQuotas maxDepth="2147483647"
                    maxStringContentLength="2147483647"
                    maxArrayLength="2147483647"
                    maxBytesPerRead="2147483647"
                    maxNameTableCharCount="2147483647" />
    </binding>
  </basicHttpBinding>
</bindings>

<behaviors>
  <serviceBehaviors>
    <behavior name="secureBehavior">
      <serviceDebug includeExceptionDetailInFaults="false" />
      <serviceMetadata httpsGetEnabled="true" />
      <dataContractSerializer maxItemsInObjectGraph="2147483647" />
    </behavior>
  </serviceBehaviors>
</behaviors>

In the service configuration you'll notice that the name doesn't quite match the name of the service controller class. That is intentional -- I'd had "Cheap Sunglasses" in my head all day -- and apparently necessary to make the whole thing work. I'll explain further in a bit. Otherwise, looks normal: service references a behavior that sets the serializer's maxItemsInObjectGraph attribute high enough to fix the original problem, and the <endpoint/> within references a basicHttpBinding that sets all of the other necessary properties.

We used to use the SecureBasic binding, but went to the AsmxConfiguration binding when we started exposing endpoints "non-secure". (HTTP instead of SSL) They're identical except that one has transport-level security.

Running the code as I've shown actually works, and without failing when returning very large data sets. However, if I remove ZZ Top from the service class name in the service configuration, the service responds with a 404 error page.
Server Error in '/v2' Application. The resource cannot be found. Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.

Requested URL: /service/

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18034

So, the questions:

  1. WHY? What inexorable forces have I set in opposition here?
  2. When writing WCF configuration for a routed service, like this one is, how do I set up the endpoint configuration? Am I doing it right? Removing the address attribute doesn't help. Should it be some other value? Basically: what is the best way to ensure that my WCF configuration is correct when I'm using routing?
Posted
Updated 31-Oct-13 9:20am
v2

1 solution

Hi,

Please find the sample Web.Config File details for Rest-Ful service.

The two main parts are responsible.

1.Services Part
    i.Service Endpoint details
2.Behaviours Part
    i.Service Behaviour
   ii.Endpoint Behaviour


Sample Web.config File:
<configuration>
  <system.web>
    <compilation debug="true" targetframework="4.0" />
  </system.web>
  <system.servicemodel>
    <services>
      <service name="WcfService3.RestImplService" behaviorconfiguration="MyServiceBehavior">
        <endpoint address="">
                  binding="webHttpBinding"
                  contract="WcfService3.IRestImplService"
                      behaviorConfiguration="MyWeb"              />
      </endpoint></service>
    </services>

    <behaviors>
      <servicebehaviors>
        <behavior name="MyServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <servicemetadata httpgetenabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <servicedebug includeexceptiondetailinfaults="true" />
        </behavior>
      </servicebehaviors>
      <endpointbehaviors>
        <behavior name="MyWeb">
          <webhttp />
        </behavior>
      </endpointbehaviors>
    </behaviors>

    <servicehostingenvironment multiplesitebindingsenabled="true" />
  </system.servicemodel>
  <system.webserver>
    <modules runallmanagedmodulesforallrequests="true" />
  </system.webserver>
</configuration>




Thanks!
 
Share this answer
 
v2
Comments
Wes Payne 1-Nov-13 20:00pm    
Thank you for responding! If there was a file attachment, I could not find it.

With only the sample configuration to go with, I am not sure that I understand the answer. I think I have a potential interpretation that is close. Please help me clarify.

1. As with my code, you add a service node to the configuration that may or may not have the same name as the class that implements the service contract, and references a ServiceBehavior (MyServiceBehavior).
2. You add that ServiceBehavior to the configuration (behaviors->servicebehaviors->behavior). I assume this is where I would set all of the service-specific behaviors I would want, as I did in my example with "secureBehavior".
3. Within the service you define a single endpoint, which I imagine covers all requests for that service. This endpoint is bound to "webHttpBinding" type, but no specific binding of that type. The endpoint has no name and an empty address, but references a behavior.
4. You add that EndpointBehavior to the configuration (behaviors->endpointbehaviors->behavior). It has a single, empty webHttp node but no other defined nodes or attributes besides the behavior name (MyWeb).

So it looks like we're adding a single endpoint for the entire REST-ful, routed service, just to have the endpoint so that we can add the webHttpBinding to it. And there is no need to set up a full binding configuration.

I assume that, once this is all done, I won't have HTTP 404 occur with every request if the service should have the same name as the class which implements its service contract.

Also, it looks like somebody else tried to copy from the same place you did. If this is copied from elsewhere, could I have a link to the full document in order to establish more context?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900