wcfrestheaderwebhttp

How do I extend WCF WebHttp (REST) to support ETags and Conditional Gets?


I have a read-only WCF REST service (all GET's baby!) I'd like to add ETag/Conditional get support to every single operation in my service.

Basically I'm interested in extending the technique in this article: http://blogs.msdn.com/b/endpoint/archive/2010/02/25/conditional-get-and-etag-support-in-wcf-webhttp-services.aspx

My site is backed by a couple of XML files, and my app knows (and raises an event) when any of them change. I don't understand where the extension points are though. How do I hook into the pipeline to add these headers for every call instead of one-at-a-time?


Solution

  • This turned out not to be so bad. I used an IDispatchMessageInspector which I hooked into a ServiceBehavior that's applied to all my services. I'm a little uncomfortable with how the request gets routed but it seems to work.

    public class ConditionalGetMessageInspector : IDispatchMessageInspector
    {
        private enum GetState { Modified, Unmodified }
    
        private string ETag { 
            get { return XmlDataLoader.LastUpdatedTicks.ToString(); }
        }
        private DateTime LastModified { 
            get { return new DateTime(XmlDataLoader.LastUpdatedTicks);}
        }
    
        public object AfterReceiveRequest(ref Message request, 
            IClientChannel channel, InstanceContext instanceContext)
        {
            try
            {
                WebOperationContext.Current.IncomingRequest
                    .CheckConditionalRetrieve(ETag);
            }
            catch (WebFaultException)
            {
                instanceContext.Abort();
                return GetState.Unmodified;
            }
            // No-op
            return GetState.Modified;
        }
    
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            if ((GetState)correlationState == GetState.Unmodified)
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode = 
                    HttpStatusCode.NotModified;
                WebOperationContext.Current.OutgoingResponse.SuppressEntityBody = 
                    true;
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.SetETag(ETag);
                WebOperationContext.Current.OutgoingResponse.LastModified = 
                    LastModified;
            }
        }
    }