How to use IDispatchMessageInspector in a WCF Service?

23.1k Views Asked by At

I am trying to use IDispatchMessageInspector in a WCF service implementation to access custom header values.

Something like:

public class MyService : IMyService
{
    public List<string> GetNames()
    {
        var headerInspector = new CustomHeaderInspector();

        // Where do request & client channel come from?
        var values = headerInspector.AfterReceiveRequest(ref request, clientChannel, OperationContext.Current.InstanceContext);            
    }
}

I've implemented my own IDispatchMessageInspector class.

public class CustomHeaderInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
        var userName = prop.Headers["Username"];

        return userName;
    }
}

How do I pass

  • System.ServiceModel.Channels.Message and

  • System.ServiceModel.IClientChannel

to AfterReceiveRequest called from the service implementation?

EDIT:

Many articles like this one or this one, give examples on how to implement your own ServiceBehavior. So your service implementation looks like this:

[MyCustomBehavior]
public class MyService : IMyService
{
    public List<string> GetNames()
    {
        // Can you use 'MyCustomBehavior' here to access the header properties?
    }
}

So with this, can I access MyCustomBehavior somehow within the service operation method to access custom header values?

5

There are 5 best solutions below

0
On

I'm using IClientMessageInspector for same goal. Here is how you can apply them from code:

 var serviceClient = new ServiceClientClass(binding, endpointAddress);
serviceClient.Endpoint.Behaviors.Add(
            new MessageInspectorEndpointBehavior<YourMessageInspectorType>());


/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class MessageInspectorEndpointBehavior<T> : IEndpointBehavior
    where T: IClientMessageInspector, new()
{
    /// <summary>
    /// Implements a modification or extension of the client across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that is to be customized.</param>
    /// <param name="clientRuntime">The client runtime to be customized.</param>
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new T());
    }

    /// <summary>
    /// Implement to pass data at runtime to bindings to support custom behavior.
    /// </summary>
    /// <param name="endpoint">The endpoint to modify.</param>
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implements a modification or extension of the service across an endpoint.
    /// </summary>
    /// <param name="endpoint">The endpoint that exposes the contract.</param>
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // Nothing special here
    }

    /// <summary>
    /// Implement to confirm that the endpoint meets some intended criteria.
    /// </summary>
    /// <param name="endpoint">The endpoint to validate.</param>
    public void Validate(ServiceEndpoint endpoint)
    {
        // Nothing special here
    }
}

And here is sample implementation of MessageInspector I'm using to pass client version to server, and retrieve server version in custom headers:

/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class VersionCheckMessageInspector : IClientMessageInspector
{
    /// <summary>
    /// Enables inspection or modification of a message before a request message is sent to a service.
    /// </summary>
    /// <param name="request">The message to be sent to the service.</param>
    /// <param name="channel">The WCF client object channel.</param>
    /// <returns>
    /// The object that is returned as the <paramref name="correlationState " /> argument of
    /// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message@,System.Object)" /> method.
    /// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
    /// <paramref name="correlationState" /> objects are the same.
    /// </returns>
    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        request.Headers.Add(new VersionMessageHeader());
        return null;
    }

    /// <summary>
    /// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
    /// </summary>
    /// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
    /// <param name="correlationState">Correlation state data.</param>
    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        var serverVersion = string.Empty;
        var idx = reply.Headers.FindHeader(VersionMessageHeader.HeaderName, VersionMessageHeader.HeaderNamespace);
        if (idx >= 0)
        {
            var versionReader = reply.Headers.GetReaderAtHeader(idx);
            while (versionReader.Name != "ServerVersion"
                   && versionReader.Read())
            {
                serverVersion = versionReader.ReadInnerXml();
                break;
            }
        }

        ValidateServerVersion(serverVersion);
    }

    private static void ValidateServerVersion(string serverVersion)
    {
        // TODO...
    }
}

public class VersionMessageHeader : MessageHeader
{
    public const string HeaderName = "VersionSoapHeader";
    public const string HeaderNamespace = "<your namespace>";
    private const string VersionElementName = "ClientVersion";

    public override string Name
    {
        get { return HeaderName; }
    }

    public override string Namespace
    {
        get { return HeaderNamespace; }
    }

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteElementString(
            VersionElementName,
            Assembly.GetExecutingAssembly().GetName().Version.ToString());
    }
}
1
On

You have to configure the

<extensions>
  <behaviorExtensions>
    <add 
      name="serviceInterceptors" 
      type="CustomHeaderInspector , MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
    />
  </behaviorExtensions>
</extensions>

Then the extension will be handled in your WCF stack. The service itself has no notion of the serviceInterceptors and you do not have to do something like in your first code block. The WCF stack will inject you Inspector.

MSDN: system.servicemodel.dispatcher.idispatchmessageinspector

0
On

On the MSDN page that you linked to there is also a description how an inspector can be inserted and also an example of that. To quote:

Typically, message inspectors are inserted by a service behavior, an endpoint behavior, or a contract behavior. The behavior then adds the message inspector to the DispatchRuntime.MessageInspectors collection.

Later on you have the following examples:

  • Implementing custom IDispatchMessageInspector
  • Implementing custom IServiceBehavior that adds the inspector to the runtime.
  • Configuration of the behavior via .config file.

That should be enough to get you started. Otherwise feel free to ask :)

If you just want to get access to headers from within your service, you could try OperationContext.Current.IncomingMessageHeaders.

0
On

I believe you don't need to implement custom IDispatchMessageInspector to retrieve custom headers, it can be done from service operation method like this:

var mp = OperationContext.Current.IncomingMessageProperties;
var property = (HttpRequestMessageProperty)mp[HttpRequestMessageProperty.Name];
var userName = property.Headers["Username"];

It makes sense to implement custom dispatch message inspector if you want to abort message processing, for example if credentials are missing - you can just throw FaultException in this case.

But if you still want to pass value from dispatch message inspector to service operation method - probably it can be passed through some singleton along with call identifier (session id), to be extracted later by method, or using wcf extensions

0
On

What I did to access the details I set the following inside IDispatchMessageInspector.AfterReceiveRequest

Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username, "Membership Provider"), roles);

I've omitted the authentication code from this.

To access the value from the service method, you can call

Thread.CurrentPrincipal.Identity.Name