WCF HTTP - Start using it!

16.02.2011 14:44Comments
WCF HTTP the new MS project in codeplex, is the new programming model to bring the HTTP closer to our Webservices. It’s still in the prototyping phase, but there’s already enough interesting code in there to give it a try and provide some feedback to the WCF team. They have done an amazing job in making our lives easier to plug HTTP specific code into the WCF stack as we’ll see in this post. We are going to go through some of the major changes in this new programming model:
  • Setting up our service
  • Service routing
  • Service contract
  • Host configuration
  • Media types support

Setting up our service

The first thing you’ll need obviously is to download the latest release from http://wcf.codeplex.com (at this point the current release is the “WCF HTTP 01.15.2011”). You’ll find the code under the “Http” folder, and a visual studio solution “Http.sln”. You can start using any of the sample projects included there, or if you want to start your own web project from scratch you can compile the solution and use the dlls from the “build” folder. Notice that there’s a change on how to set up WCF HTTP in the web.config, the only thing that is required now is the routing module, and ASP .NET compatibility:
<configuration>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </modules>
  </system.webServer>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
</configuration>

Service routing

When working with the WCF HTTP bits, you are going to route the requests to a certain service using routes. You can use the AddServiceRoute extension method, to define the prefix of the service, the type of the service, and an HttpHostConfiguration (more on this later). So here’s the basic setup:
var configuration = new HttpHostConfiguration();
RouteTable.Routes.AddServiceRoute<ContactsResource>("contacts", configuration);
So if the web app is running on our 5555 port, then all of the calls done to http://localhost:5555/contacts will be routed to the ContactsResource service. You can add as many routes as you need routing to as many services as you need. The ContactsResource is a typical REST service contract like:
[ServiceContract]
    public class ContactsResource
    {
        private readonly IContactRepository repository;

        public ContactsResource(IContactRepository repository)
        {
            this.repository = repository;
        }

        [WebGet(UriTemplate = "{id}")]
        public Contact Get(string id)
        {
            return this.repository.Get(int.Parse(id));
        }

    }
This brings us to the next topic…

Service contract

In WCF HTTP you have the ability to add two optional parameters to your operations and they will be “magically” injected there: HttpRequestMessage and HttpResponseMessage. This is very convient for manipulating the StatusCode and content of the response. This allows us to do something like this:
[WebGet(UriTemplate = "{id}")]
        public Contact Get(int id, HttpResponseMessage response)
        {

            var contact = this.repository.Get(id);
            if (contact == null)
            {
                response.StatusCode = HttpStatusCode.NotFound;
                response.Content = new StringContent("Contact not found");
            }

            return contact;
        }
Notice how you can set the StatusCode in the response directly, and how you can manipulate the content from within the service. In a transport-agnostic platform like WCF, it was quite difficult to do something like this. By adding a HttpRequestMessage parameter to your operation you can also gain access to stuff like headers and Urls, which can also be convient to have. The other awesome feature they brought us in this release is the QueryComposition attribute which you can use over IEnumerable<T> operations like this:
[WebGet(UriTemplate="")]
        [QueryComposition]
        public IEnumerable<Contact> Get()
        {
            return contacts.AsQueryable();
        }
By doing this you can now compose filters over this resource using the OData protocol. In our contacts sample you could filter the contacts by Name by browsing this Url: http://localhost:5555/Contacts?$filter=Name eq ‘First Contact’ (you might have to encode the espace with %20) You can even compose filters by navigating relationships using the OData syntax. Here’s how you could get all the contacts from US: http://localhost:5555/Contacts?$filter=Address/City/State/Country eq ‘US’ This only applies to filtering, sorting and paging, so you can also use the $top, $skip, $orderby, but you can’t use $select (for projections) and $expand.

Host configuration

WCF HTTP made our lives easy for some of the common tasks when working with HTTP web services like: IoC/DI containers integration, media types support, serialization, etc. In order to shape your web service, all you need to do is provide a custom HttpHostConfiguration instance. Let’s look at the Contacts sample that ships in the WCF HTTP download. The ContactManagerConfiguration class uses IoC/DI with MEF to instantiate the services. It takes a CompositionContainer and implements IInstanceFactory interface that defines the following two methods:
public interface IInstanceFactory
    {
        object GetInstance(Type serviceType, InstanceContext instanceContext, Message message);
         void ReleaseInstance(InstanceContext instanceContext, object service);
    }
The serviceType parameter is the one we need to get an instance of and return it in the GetInstance method. Here’s how it’s don using MEF’s CompositionContainer:
public object GetInstance(Type serviceType, InstanceContext instanceContext, Message message)
        {
            var contract = AttributedModelServices.GetContractName(serviceType);
            var identity = AttributedModelServices.GetTypeIdentity(serviceType);

            // force non-shared so that every service doesn't need to have a [PartCreationPolicy] attribute.
            var definition = new ContractBasedImportDefinition(contract, identity, null, ImportCardinality.ExactlyOne, false, false, CreationPolicy.NonShared);
            return this.container.GetExports(definition).First().Value;
        }
You could easily change this implementation to use Windsor, Unity, StructureMap, Sprint.NET, etc. The other extremely useful thing we can do in the HttpHostConfiguration is to shape the operations processors list (both for request and response), but we’ll see how to do that in the next section.

Media types support

One of the important things about REST services is to provide a useful representation of the resources based on what the clients are requesting in the Accept header. So if a client “understands” only the json format, there’s no point in returning an xml representation. In order to make our lives easier, WCF HTTP ships with a base type called MediaTypeProcessor that allows us to define the media types we can handle, and serialize/deserialize to a certain format if the client accepts it. We can take the PngProcessor from the “Contacts” sample project for instance:
public class PngProcessor : MediaTypeProcessor
    {
        public PngProcessor(HttpOperationDescription operation, MediaTypeProcessorMode mode)
            : base(operation, mode)
        {
        }

        public override IEnumerable&lt;string&gt; SupportedMediaTypes
        {
            get
            {
                yield return "image/png";
            }
        }

        public override void WriteToStream(object instance, Stream stream, HttpRequestMessage request)
        {
            var contact = instance as Contact;
            if (contact != null)
            {
                var path = string.Format(CultureInfo.InvariantCulture, @"{0}binImagesImage{1}.png", AppDomain.CurrentDomain.BaseDirectory, (contact.ContactId % 3) + 1);
                using (var fileStream = new FileStream(path, FileMode.Open))
                {
                    byte[] bytes = new byte[fileStream.Length];
                    fileStream.Read(bytes, 0, (int)fileStream.Length);
                    stream.Write(bytes, 0, (int)fileStream.Length);
                }
            }
        }

        public override object ReadFromStream(Stream stream, HttpRequestMessage request)
        {
            throw new NotImplementedException();
        }
    }
The SupportedMediaTypes property gives WCF a hint of what formats we can serilialize/deserialize. If a user sends a request with an Accept header of “image/png”, then the PngProcessor will be called to serialize the response to png, thus the code in the WriteToStream. And if the client send a POST/PUT with a Content-type of “image/png”, then the ReadFromStream will be executed in the PngProcessor. Which in this case throws a NotImplementedException. The PngProcessor is a special type of Processor (it’s a MediaTypeProcessor), so it can be added to the processors pipeline through the HttpHostConfiguration by implementing IProcessorProvider:
public interface IProcessorProvider
    {
        void RegisterRequestProcessorsForOperation(HttpOperationDescription operation, IList&lt;Processor&gt; processors, MediaTypeProcessorMode mode);

        void RegisterResponseProcessorsForOperation(HttpOperationDescription operation, IList&lt;Processor&gt; processors, MediaTypeProcessorMode mode);
    }
And finally adding a new instance of the processor in the response pipeline:
public void RegisterResponseProcessorsForOperation(HttpOperationDescription operation, IList&lt;Processor&gt; processors, MediaTypeProcessorMode mode)
        {
            processors.Add(new PngProcessor(operation, mode));
        }
There are all nice-to-have features that are made a lot easier to implement using WCF. Of course we keep on having the rest of the extensibility points WCF provides, however the WCF HTTP team is making an excellent job keeping us away from those :)

comments powered by Disqus