Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use IDispatchMessageInspector in a WCF Service?

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?

like image 920
Didaxis Avatar asked Jun 13 '14 21:06

Didaxis


2 Answers

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

like image 134
Peter Avatar answered Sep 28 '22 03:09

Peter


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());
    }
}
like image 32
Woodman Avatar answered Sep 28 '22 05:09

Woodman