WCF Middleware/Interceptor for returning custom response earlier

218 Views Asked by At

What i want to achieve:

I want to controll all calls to WCF methods and add some validation. If the validation fails then i want the method to return earlier with a custom response of the same type with the method return type.

How can i achieve this in central place without having to add the validation check in every method?

Here is the pseudocode:

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Text;
using System.Threading;

namespace Microsoft.WCF.Documentation
{
  [ServiceContractAttribute(Namespace="http://microsoft.wcf.documentation")]
  public interface ISampleService
  {
    [OperationContractAttribute(AsyncPattern=true)]
    IAsyncResult BeginGetCustomerInfo(GetCustomerInfoRequest request, AsyncCallback callback, object asyncState);

    GetCustomerInfoResponse EndGetCustomerInfo(IAsyncResult result);

    [OperationContractAttribute(AsyncPattern=true)]
    IAsyncResult BeginGetProductDetails(GetProductDetailsRequest request, AsyncCallback callback, object asyncState);

    GetProductDetailsResponse EndGetProductDetails(IAsyncResult result);
  }

  public class SampleService : ISampleService
  {
    public IAsyncResult BeginGetCustomerInfo(GetCustomerInfoRequest request, AsyncCallback callback, object asyncState)
    {
      Console.WriteLine("BeginGetCustomerInfo called with: \"{0}\"", request);
      //...
    }

    public GetCustomerInfoResponse EndGetCustomerInfo(IAsyncResult r)
    {
        //...
        return new GetCustomerInfoResponse();
    }

    public IAsyncResult BeginGetProductDetails(GetProductDetailsRequest request, AsyncCallback callback, object asyncState)
    {
      Console.WriteLine("BeginGetProductDetails called with: \"{0}\"", request);
      //...
    }

    public GetProductDetailsResponse EndGetProductDetails(IAsyncResult r)
    {
        //...
        return new GetProductDetailsResponse();
    }
  }
}
2

There are 2 best solutions below

0
On

You can use a WCF message interceptor. A message interceptor allows you to intercept incoming and outgoing messages in a WCF service.

You need to add a class which implements the IDispatchMessageInspector interface, let's say ValidationInterceptor. your validation logic should be implemented in AfterReceiveRequest method.

And a class which implements Attribute, IServiceBehavior, you can write your logic in ApplyDispatchBehavior method like the below.

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
    foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
    {
        ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
        if (channelDispatcher != null)
        {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
            {
                endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new ValidationInterceptor());
            }
        }
    }
}

And use like this

[ValidationInterceptor]
public IAsyncResult BeginGetCustomerInfo(GetCustomerInfoRequest request, AsyncCallback callback, object asyncState)
{
    Console.WriteLine("BeginGetCustomerInfo called with: \"{0}\"", request);
    //...
}
0
On

By implementing IDispatchMessageInspector interface and use AfterReceiveRequest and BeforeSendReply this two methods to receive requests and logic operation before the request is sent.

Under AfterReceiveReply there is the ValidateMessageBody method, which wraps a validation XmlReader around the body content subtree of the passed message like this:

object IDispatchMessageInspector.AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
    if (validateRequest)
    {
        // inspect the message. If a validation error occurs,
        // the thrown fault exception bubbles up.
        ValidateMessageBody(ref request, true);
    }
    return null;
}