RESTful WCF services without .svc file and with very little configuration

WCF is, on the whole, pretty neat. It’s a doddle to decorate methods with UriTemplates and specify DataMembers. Building the actual meat of a web service is made very easy by the wealth of functionality available in WCF4, and this is the bit I usually happily chip together whenever I need to write a RESTful web service.

Then I get to the part where I have to add the service’s endpoint to web.config. It’s usually been several months since I last did it. I look it up on google, but the results don’t quite seem to apply to me. I open up an old project where I last did this ridiculous dance, but evidently I’m missing something because the damned thing still won’t work. The problem is most likely a subtly mis-set attribute. Or may web.config is just a monster which will eat us all.

With that in mind, I was recently delighted to find this blog post by Steve Michelotti. It’s a little old, but the knowledge within was new to me, so it may be new to you too.

In a nutshell, it leverages the WebServiceHostFactory class to allow a ServiceRoute to your WCF service to be defined. The advantages of this are two-fold:

  • No service-specific entries are needed in web.config
  • The WCF service can be defined in a plain C# class rather than a .svc file.

Steve covers this in much more detail in his blog post, so what follows is a quick summary of the steps required to get a service up and running:

  • Define your service in a C# class (implementing an interface is recommended but not required).
  • Decorate your class with
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    This is required for the service to operate over HTTP.

  • A teensy bit of web.config tweaking is required. This is the other half of the configuration above.
    <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    </system.serviceModel>
  • Add a reference to System.ServiceModel.Activation. This is not to be confused with System.ServiceModel.Activision.
  • To add the ServiceRoute, add a line similar to the one below to Application_Start in Global.asax.cs:
    RouteTable.Routes.Add(new ServiceRoute("Services", new WebServiceHostFactory(), typeof(Your_Service_Class)));

    Where Your_Service_Class is the name of the class you previously wrote for your service. Leaving the first parameter of ServiceRoute blank results in all requests to the service being relative to the site route. Services in the example above is the route prefix of your service url. You can leave it blank, but I prefer to specify a path to keep my services tidily organised.  You should decorate your service methods with a suitable UriTemplate, eg

    [WebGet(UriTemplate = "MyService", ResponseFormat = WebMessageFormat.Json)]

    In the above case the service can be called via a browser on http://my_hostname/services/myservice

That’s it! I can now get on with the meaty business of writing code instead of wrestling with the beast that is web.config.

In the meantime, here are some goats in a tree.

Goats in a tree

2 comments

  1. JR Jimenez says:

    Hi – thanks for great article. I have it working for JSON calls to my service, now I’m trying to activate JSONP. To do this I need to assign a BindingConfiguration to my Service Endpoint.

    The endpoint is created as:
    RouteTable.Routes.Add(new ServiceRoute(“Svc1”, new WebServiceHostFactory(), typeof(DataService1)));

    The BindingConfiguration to be used is in the web.config:

    Any idea how to associate the BindingConfiguration webHttpBindingWithJsonP to the service endpoint I created in code?

    • Squawky says:

      Sorry for the late reply but my notifications seem to have dropped dead. 😉 You’ve probably already solved this or worked around it, and it has been a while since I’ve touched this stuff, but I *think* you need to add an endpoint for the binding in web.config with its address set to your service route. Something like this:

      <system.serviceModel>
        <behaviors>
          <endpointBehaviors>
            <behavior name="webHttpBehavior">
              <webHttp />
            </behavior>
          </endpointBehaviors>
        </behaviors>
        <bindings>
          <webHttpBinding>
            <binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true" />
          </webHttpBinding>
        </bindings>
        <services>
          <service name="DataService1">
            <endpoint address="/svc1" binding="webHttpBinding"
                      bindingConfiguration="webHttpBindingWithJsonP"
                      contract="DataService1"
                      behaviorConfiguration="webHttpBehavior"/>
          </service>
        </services>
      </system.serviceModel>
      

      DataService1 will need prefixing with its full namespace.

Leave a Reply

Your email address will not be published. Required fields are marked *