Monday, July 25, 2011

Working with WCF Web Api 4.0 for Hypermedia (HATEOAS)


I wanted to enable Hypermedia - (HATEOAS) support in an existing WCF REST Service as a POC.

Steve, has a really nice post on how to go about this. Did pretty much everything mentioned. But my REST Service used POCO classes as both the EF 4.0 Model as well as DataContract, (just for the sake of faster turnaround time of the POC) and a  sample of the POCO looks like :

    [Serializable]
    [DataContract]
    [KnownType(typeof(Quote))]
    [KnownType(typeof(Address))]
    public class Customer
    {
        [Key]
        [DataMember]
        public int CustomerID { get; set; }
        [DataMember]
        public string PrimaryInsurerFirstName { get; set; }
        [DataMember]
        public string PrimaryInsurerLastName { get; set; }
        [DataMember]
        public int PrimaryInsurerAge { get; set; }
        [DataMember]
        public virtual ICollection<Address> Address { get; set; }
        [DataMember]
        public virtual ICollection<Quote> Quote { get; set; }
          }

As per Steve’sCode sample, used WCF Web API 4 and included necessary Media Type Formatters and all the associated infrastructure. Instead of typical 
RouteTable.Routes.Add(new ServiceRoute("Service", new StructureMapServiceHostFactory(), typeof(Service)));

i used RouteTable.Routes.MapServiceRoute<Service>("msi", config); (WCF Web Api extensions )

But as soon as i try to access the root of the service (http://localhost/Service) got the following exception, and it was not at all hosting the REST Service as before :





Looking at the error its clear that Serialization of Interface is not possible using XMLSerializer, hence using List<Address> solved the error. :) 

But the question is why did this error occur? Earlier i had the same Datacontract and it worked perfectly fine. So what happened when i implemented WCF Web API 4.0 .


First thing was to figure out why the service is not getting Hosted/Started properly. Ideally WCF should throw error when we try to call  Operation/Method which returns or accepts Customer, a runtime exception maybe. But this was more of Compilation Error in a way,  that for any request associated with this RestService , it was not able to start the service. 

Hence using reflector, and tracing out the entire WCF Request Processing Pipeline,

I conclude that :

          In order to Host/Start a Service, the runtime loops through all the Operations in a ServiceContract and makes sure that Service is well formed before it accepts any incoming requests. This is accomplished using  “DispatcherBuilder.BindOperations()”

          WCF Web API extends typical WCF Dispatch Behavior and uses XmlFormatter by default, while WCF 4.0 uses DataContractSerializer by default.


As per above If i comment out the Method which works with Customer Object, everything works fine and the service gets hosted as before without issues. But we just cant do that!!

Now we can solve this by :
1.  Change all the Interfaces to Concrete Implementation in the Customer POCO.

2.  Add the [DataContractFormat] attribute for the Service Contract and override the default XMLSerializer used by WCF Web API 4.0.




WCF REST Request Processing Pipeline

Generally we dont feel the need to know what exactly happens under the hood when you specify a REST Endpoint  while hosting your REST Service , 
RouteTable.Routes.Add(new ServiceRoute("CustomerExperienceService", new StructureMapServiceHostFactory(), typeof(CustomerExperienceService)));

We take for granted that WCF Runtime will host the service endpoint for us and it will be work as expected.


But i encountered a scenario, wherein the service wont start/hosted at all due to a error.  It just didnt hit any of breakpoints in the ServiceContract constructor or any of the Operations (Methods) , surely something was wrong with ServiceHosting process.

Hence using Reflector, i decided to take on the challenge of deciphering the code that makes our lives so easy. "A picture is worth a thousand words",  hence  a  block diagram would make it easy to understand : 

One important learning that i would want to share here is that , because of    

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <standardEndpoints>
      <webHttpEndpoint>
        <!--
            Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
            via the attributes on the <standardEndpoint> element below
        -->
        <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
      </webHttpEndpoint>
    </standardEndpoints>
  </system.serviceModel>

the runtime calls DispatcherBuilder.InitializeServiceHost() only when there is a request for a particular REST Service, pretty much same like Asp.Net application. 

And when DispatcherBuilder.InitializeServiceHost() calls DispatcherBuilder.BindOperations() , all of the operation contracts under a ServiceContract are looped to ensure that WCF is able to process any request that comes in. The appropriate Formatters (XML or DataContract Serializers) are identified for each of the Operations and a check is performed whether Serialization/Deserialization of parameters is possible or not. 

Hence in the scenario i mentioned in other post, if i comment out the Operation which accepts or returns the Customer Object, the Service was getting hosted fine without any errors.

To conclude :  while Hosting the REST Service, the WCF Runtime loops through all of the Operations (via  DispatcherBuilder.BindOperations() ) you have in a ServiceContract and as it goes along, it also makes sure that Serialization of parameters is possible so that it can process any subsequent requests. Only if this check is success , the service will be hosted otherwise you will  see the specific error without any breakpoints being hit on your ServiceContract.