This sample was provided as part of a blog series on WCF extensibility. The entry for this sample can be found at http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/10/wcf-extensibility-operation-selectors.aspx.

This issue came from the forum. REST services we use HTTP verbs (methods) to determine the action to be applied to the resource. For example, a GET request to /Contacts/1 would return the contact with id 1, while DELETE /Contacts/1 would remove that same contact from the repository. This is a great – and simple – model, but there are some browsers, proxies, firewalls or even some web service platforms which don’t support the full set of verbs (rejecting anything other than GET and POST). In order to overcome this problem, some platforms (such as ASP.NET MVC and Google Data) started supporting a new HTTP header or field, called X-HTTP-Method-Override, which contain the actual method which should be used by the service to access the resource. The WCF REST API doesn’t support this override natively, but that doesn’t preclude us from using an extensibility point to enable it.

Implementing the support for X-HTTP-Method-Override is nothing but a matter of replacing the operation selector (based on methods and URI templates) used by REST endpoints with our own, which is aware of this new header. When the IDispatchOperationSelector.SelectOperation method is called, we can check if the incoming request contained the override header, and if so, we can update the incoming message properties to let the original selector chose the appropriate operation.

For this example I’ll reuse the contact manager service from the message inspectors post – it contains the different operations which differ based on the HTTP verb of the incoming request, so it’s perfect for this issue. And before I start, the usual disclaimer – this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). Also, for simplicity sake it doesn’t have a lot of error handling which a production-level code would. Also, the contact manager is all stored in memory, a “real” one would have a backing database or something more “persistent”.

One small parenthesis: the link below for the code in this post contains the version shown here in C# and also a version in VB.NET which does the same thing; if you’re a VB developer, you can check the MSDN code samples out.

The contracts and service implementation are exactly the same as the one from before (I removed the version for Text, as it didn’t add any value to this topic):

C#Visual Basic
Edit|Remove
    [DataContract] 
    public class Contact 
    { 
        [DataMember] 
        public string Id { getset; } 
        [DataMember] 
        public string Name { getset; } 
        [DataMember] 
        public string Email { getset; } 
        [DataMember] 
        public string[] Telephones { getset; } 
    } 
 
    [ServiceContract] 
    public interface IContactManager 
    { 
        [WebInvoke( 
            Method = "POST", 
            UriTemplate = "/Contacts", 
            ResponseFormat = WebMessageFormat.Json)] 
        string AddContact(Contact contact); 
 
        [WebInvoke( 
            Method = "PUT", 
            UriTemplate = "/Contacts/{id}", 
            ResponseFormat = WebMessageFormat.Json)] 
        void UpdateContact(string id, Contact contact); 
 
        [WebInvoke( 
            Method = "DELETE", 
            UriTemplate = "/Contacts/{id}", 
            ResponseFormat = WebMessageFormat.Json)] 
        void DeleteContact(string id); 
 
        [WebGet(UriTemplate = "/Contacts", ResponseFormat = WebMessageFormat.Json)] 
        List<Contact> GetAllContacts(); 
 
        [WebGet(UriTemplate = "/Contacts/{id}", ResponseFormat = WebMessageFormat.Json)] 
        Contact GetContact(string id); 
    } 
 
    public class ContactManagerService : IContactManager 
    { 
        static List<Contact> AllContacts = new List<Contact>(); 
        static int currentId = 0; 
        static object syncRoot = new object(); 
 
        public string AddContact(Contact contact) 
        { 
            int contactId = Interlocked.Increment(ref currentId); 
            contact.Id = contactId.ToString(CultureInfo.InvariantCulture); 
            lock (syncRoot) 
            { 
                AllContacts.Add(contact); 
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Created; 
            } 
 
            return contact.Id; 
        } 
 
        public void UpdateContact(string id, Contact contact) 
        { 
            contact.Id = id; 
            lock (syncRoot) 
            { 
                int index = this.FetchContact(id); 
                if (index >= 0) 
                { 
                    AllContacts[index] = contact; 
                } 
            } 
        } 
 
        public void DeleteContact(string id) 
        { 
            lock (syncRoot) 
            { 
                int index = this.FetchContact(id); 
                if (index >= 0) 
                { 
                    AllContacts.RemoveAt(index); 
                } 
            } 
        } 
 
        public List<Contact> GetAllContacts() 
        { 
            List<Contact> result; 
            lock (syncRoot) 
            { 
                result = AllContacts.ToList(); 
            } 
 
            return result; 
        } 
 
        public Contact GetContact(string id) 
        { 
            Contact result; 
            lock (syncRoot) 
            { 
                int index = this.FetchContact(id); 
                result = index < 0 ? null : AllContacts[index]; 
            } 
 
            return result; 
        } 
 
        private int FetchContact(string id) 
        { 
            int result = -1; 
            for (int i = 0; i < AllContacts.Count; i++) 
            { 
                if (AllContacts[i].Id == id) 
                { 
                    result = i; 
                    break; 
                } 
            } 
 
            if (result < 0) 
            { 
                WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound; 
            } 
 
            return result; 
        } 
    } 
 
 

Now for the behavior used to set up the operation selector. The behavior wraps the original operation selector from the endpoint, so it needs to be added after the WebHttpBehavior (which will add the first selector to the runtime) – see the discussion about wrapping internal objects at the post about formatters).

C#Visual Basic
Edit|Remove
public class HttpOverrideBehavior : IEndpointBehavior 
{ 
    public const string HttpMethodOverrideHeaderName = "X-HTTP-Method-Override"; 
    public const string OriginalHttpMethodPropertyName = "OriginalHttpMethod"; 
 
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
    } 
 
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
    } 
 
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
        endpointDispatcher.DispatchRuntime.OperationSelector = new HttpOverrideOperationSelector(endpointDispatcher.DispatchRuntime.OperationSelector); 
    } 
 
    public void Validate(ServiceEndpoint endpoint) 
    { 
    } 
} 
 
 

And the selector itself. As I mentioned before, The first thing the selector will do is to try to fetch the HttpRequestMessageProperty from the message properties – if the message was received using HTTP, it will contain information about the HTTP verb and headers in the request. It will then try to find the X-HTTP-Method-Override header, and if it’s present, it will update the message property with the method override. For completeness sake, this selector also adds a new property to the message with the original HTTP verb from the request (if the service needs this information somehow). Finally, the selector delegates the selection to the original selector, which will then use that information to route the incoming request to the appropriate method.

C#Visual Basic
Edit|Remove
class HttpOverrideOperationSelector : IDispatchOperationSelector 
{ 
    private IDispatchOperationSelector originalSelector; 
 
    public HttpOverrideOperationSelector(IDispatchOperationSelector originalSelector) 
    { 
        this.originalSelector = originalSelector; 
    } 
 
    public string SelectOperation(ref Message message) 
    { 
        if (message.Properties.ContainsKey(HttpRequestMessageProperty.Name)) 
        { 
            HttpRequestMessageProperty reqProp; 
            reqProp = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name]; 
            string httpMethodOverride = reqProp.Headers[HttpOverrideBehavior.HttpMethodOverrideHeaderName]; 
            if (!String.IsNullOrEmpty(httpMethodOverride)) 
            { 
                message.Properties[HttpOverrideBehavior.OriginalHttpMethodPropertyName] = reqProp.Method; 
                reqProp.Method = httpMethodOverride; 
            } 
        } 
 
        return this.originalSelector.SelectOperation(ref message); 
    } 
} 
 
 

Finally, the test code to wrap it all together. The code is very similar to the one for the message inspectors post, with a few differences: the new behavior (HttpOverrideBehavior) is added after the REST ((WebHttp) behavior, and after sending requests with the “real” HTTP verb it will send some additional requests using POST, but using the HTTP override method to route it to different operations.

C#Visual Basic
Edit|Remove
static void Main(string[] args) 
{ 
    string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
    ServiceHost host = new ServiceHost(typeof(ContactManagerService), new Uri(baseAddress)); 
    ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IContactManager), new WebHttpBinding(), ""); 
    endpoint.Behaviors.Add(new WebHttpBehavior()); 
    endpoint.Behaviors.Add(new HttpOverrideBehavior()); 
    host.Open(); 
    Console.WriteLine("Host opened"); 
 
    string johnId = SendRequest( 
        "POST", 
        baseAddress + "/Contacts", 
        "application/json", 
        CreateJsonContact(null"John Doe""john@doe.com""206-555-3333")); 
    string janeId = SendRequest( 
        "POST", 
        baseAddress + "/Contacts", 
        "application/json", 
        CreateJsonContact(null"Jane Roe""jane@roe.com""202-555-4444 202-555-8888")); 
 
    Console.WriteLine("All contacts"); 
    SendRequest("GET", baseAddress + "/Contacts"nullnull); 
 
    Console.WriteLine("Updating Jane"); 
    SendRequest( 
        "PUT", 
        baseAddress + "/Contacts/" + janeId, 
        "application/json", 
        CreateJsonContact(janeId, "Jane Roe""jane@roe.org""202-555-4444 202-555-8888")); 
 
    Console.WriteLine("All contacts"); 
    SendRequest("GET", baseAddress + "/Contacts"nullnull); 
 
    Console.WriteLine("Deleting John"); 
    SendRequest("DELETE", baseAddress + "/Contacts/" + johnId, nullnull); 
 
    Console.WriteLine("Is John still here?"); 
    SendRequest("GET", baseAddress + "/Contacts/" + johnId, nullnull); 
 
    Console.WriteLine("Adding John again"); 
    johnId = SendRequest( 
        "POST", 
        baseAddress + "/Contacts", 
        "application/json", 
        CreateJsonContact(null"John Doe""john@doe.com""206-555-3333")); 
 
    Console.WriteLine("Updating John, now using X-HTTP-Method-Override"); 
    Dictionary<stringstring> overrideWithPut = new Dictionary<stringstring>(); 
    overrideWithPut.Add("X-HTTP-Method-Override""PUT"); 
    SendRequest( 
        "POST", 
        baseAddress + "/Contacts/" + johnId, 
        "application/json", 
        CreateJsonContact(johnId, "John Doe Updated""john@doe.com""206-555-3333"), 
        overrideWithPut); 
 
    Console.WriteLine("All contacts"); 
    SendRequest("GET", baseAddress + "/Contacts"nullnull); 
 
    Console.WriteLine("Deleting Jane, using X-HTTP-Method-Override"); 
    Dictionary<stringstring> overrideWithDelete = new Dictionary<stringstring>(); 
    overrideWithDelete.Add("X-HTTP-Method-Override""DELETE"); 
    SendRequest("POST", baseAddress + "/Contacts/" + janeId, "application/json""", overrideWithDelete); 
 
    Console.WriteLine("All contacts"); 
    SendRequest("GET", baseAddress + "/Contacts"nullnull); 
 
    Console.WriteLine("Press ENTER to close"); 
    Console.ReadLine(); 
    host.Close(); 
} 
 
 

[Go to the blog post]