Extending RESTful services with a custom Request Interceptor

There are a lot of aspects that needs to be combined in order to architect robust RESTful services to go beyond the typical HTTP verb and URI modeling. Capabilities such as Low-Hi REST, Error Handling or Caching are fundamental elements of real world RESTful solutions. On windows Communication Foundation (WCF) developers typically implement those capabilities by leveraging elements of the dispatcher runtime extensibility programming model like message inspectors, message filters or error handlers. The recently announced REST Starter Kit introduces another extensibility point in the form of request interceptors that tremendously simplifies the experience for enhancing RESTful services with extra capabilities that are not included in the default programming model.

Why another extensibility point?

Last July I presented a MSDN WebCast about WCF extensibility on which I demonstrated more that twenty extensibility points in the WCF runtime. So you might be questioning why in the world do we need an extra extensibility point instead of leveraging the existing ones.

The simple answer to that question take us back to the nature of RESTful services and its relationship with the HTTP protocol. If we analyze the characteristics of typical RESTful services we can immediate notice that complementary features such as caching, low-high REST or routing are intrinsically related to elements of the HTTP protocol like Headers or URIs. Looking at this from a Web Services engine's standpoint, the logic that implements those features should take place right after the HTTP protocol elements are processed. In the case of WCF, this is a typical use of protocol channels but from a pragmatic viewpoint this entails that we might end up using five or six channels to achieve the functionality required by a typical RESTful service. Additionally,  we need to consider that WCF protocol channels need to include a lot of infrastructure logic like state management which are not exactly relevant to the features we are trying to implement. In other words, our ideal solution will be to have an extensibility point that executes at the channel level and that it does not present the complexities of a WCF channel.

In order to address these challenges, the REST Starter Kit introduces the concept of a request interceptor which is a component that executes at the channel level and are provided with access to the entire request stack. Differently from other extensibility points, request interceptors are executed by a single channel at very early in the WCF Channel pipeline and before other extensibility components. Request interceptors are abstracted by the RequestInterceptor class which provides an very simple interface for processing the request context on both synchronous and asynchronous as illustrated in the following figure:

public abstract class RequestInterceptor
{
    ...
    // Methods
    protected RequestInterceptor(bool isSynchronous);
    public virtual IAsyncResult BeginProcessRequest(RequestContext context, AsyncCallback callback, object state);
    public virtual RequestContext EndProcessRequest(IAsyncResult result);
    public abstract void ProcessRequest(ref RequestContext requestContext);
    ...
}

As we explained before, request interceptors are executed at the channel level. Specifically, request interceptors are executed by the RequestInterceptorReplyChannelBase which provides the base functionality for the request interception capabilities included in the REST Starter Kit. As it names indicates this channel is the base class for all the channels for the different interceptor channels implemented by the REST Starter Kit.

internal abstract class RequestInterceptorReplyChannelBase<TChannel> : ChannelBase, IReplyChannel, IChannel, ICommunicationObject where TChannel: IReplyChannel
{
...
  public RequestInterceptorReplyChannelBase(ChannelManagerBase channelManager, TChannel inner,   Collection<RequestInterceptor> interceptors);

...
}

Following the WCF programming model, these channels are enabled by an instance of the RequestInterceptorBindingElement WCF binding element.

public class RequestInterceptorBindingElement : BindingElement
{
    ...
    public RequestInterceptorBindingElement(Collection<RequestInterceptor> interceptors);

    ....
    public Collection<RequestInterceptor> Interceptors { get; }

    ...
}

Finally, this binding element is inserted into the binding element collection by the WebServiceHost2 custom host which injects most of the extensibility components provided by the REST Starter Kit.

public class WebServiceHost2 : WebServiceHost
{

...

protected override void OnOpening()
{

...

  if (this.Interceptors.Count > 0)
    {
        binding.Elements.Insert(0, new RequestInterceptorBindingElement(this.Interceptors));
    }

...

}

...

}

Implementing a sample request interceptor

The fundamental step for creating a request interceptor is implementing the ProcessRequest method and, if necessary, the equivalent asynchronous functionality. The following code illustrates a skeleton of a WCF request interceptor.

public class SampleInterceptor: Microsoft.ServiceModel.Web.RequestInterceptor
    {
        public SampleInterceptor()
            : base(true)
        { }

       public override void ProcessRequest(ref System.ServiceModel.Channels.RequestContext requestContext)
        {
           ...Interceptor's synchronous implementation...
        }

       public override IAsyncResult BeginProcessRequest(RequestContext context, AsyncCallback callback, object state)
        {
             ...Interceptor's begin synchronous implementation...           
              return base.BeginProcessRequest(context, callback, state);
        }

        public override RequestContext EndProcessRequest(IAsyncResult result)
        {
            ...Interceptor's end asynchronous implementation...
            return base.EndProcessRequest(result);
        }

    }

In order to enable a custom request interceptor we need to add it to the WebServiceHost2's interceptor list explored in the previous section. One of the easiest way to accomplish this is implementing a new host factory that overrides the initialization of this host.

public class SampleHostFactory: WebServiceHost2Factory
{
    public SampleHostFactory():base()
    {
    }

    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
    {
        WebServiceHost2 host = (WebServiceHost2)base.CreateServiceHost(constructorString, baseAddresses);
        host.Interceptors.Add(new SampleInterceptor());
        return host;
    }
}

At this point the request interceptor channel is initialized with the list of interceptors that need to be invoke it when a message is received. 

 

 

 

 

3 Comments

  • This is really neat.

    Can the same concept work for Client requests? Case in point, I am building a client in WCF for a non-WCF REST service that requires POSTs and PUTs to be x-form-urlencoded. But those requests will return POX, just like GET requests. So how can I format the outbound call accordingly in WCF?

    Thanks!

    Robert McLaws
    Interscape Technologies, Inc.

  • How can I access the HttpContext inside the ProcessRequest method of the requestInterceptor?

  • Is the RequestInterceptor extensibility point available in WCF 4.0? I haven't found it

Comments have been disabled for this content.