如何在WCF服务中使用IDispatchMessageInspector

本文关键字:IDispatchMessageInspector 服务 WCF | 更新日期: 2023-09-27 18:08:46

我试图在WCF服务实现中使用IDispatchMessageInspector来访问自定义头值。

类似:

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);            
    }
}

我已经实现了自己的IDispatchMessageInspector类。

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;
    }
}

如何传递

  • System.ServiceModel.Channels。消息

  • System.ServiceModel。 IClientChannel

AfterReceiveRequest从服务实现调用 ?

编辑:

许多像这篇或这篇这样的文章都给出了如何实现自己的ServiceBehavior的示例。因此,您的服务实现看起来像这样:

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

那么,我可以在服务操作方法中以某种方式访问MyCustomBehavior以访问自定义头值吗?

如何在WCF服务中使用IDispatchMessageInspector

你必须配置

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

那么扩展将在WCF堆栈中处理。服务本身没有serviceInterceptors的概念,您不必在第一个代码块中做类似的事情。WCF栈将注入你的检查器。

MSDN: system.servicemodel.dispatcher.idispatchmessageinspector

我使用IClientMessageInspector为相同的目标。下面是如何从代码中应用它们的方法:

 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
    }
}

这里是MessageInspector的示例实现,我使用它将客户端版本传递给服务器,并在自定义报头中检索服务器版本:

/// <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());
    }
}

我相信你不需要实现自定义IDispatchMessageInspector来检索自定义标头,它可以从服务操作方法中完成,如下所示:

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

实现自定义调度消息检查器是有意义的,如果你想中止消息处理,例如,如果凭据丢失——在这种情况下,你可以抛出FaultException。

但是如果您仍然希望将值从调度消息检查器传递到服务操作方法-可能它可以通过一些单例与调用标识符(会话id)一起传递,稍后通过方法提取,或者使用wcf扩展

为了访问详细信息,我在IDispatchMessageInspector.AfterReceiveRequest

中设置了以下内容

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

我省略了验证码。

要从service方法访问该值,可以调用

Thread.CurrentPrincipal.Identity.Name

在你链接到的MSDN页面上也有一个如何插入检查器的描述和一个示例。引用:

通常,消息检查器由服务行为、端点行为或契约行为插入。然后,该行为将消息检查器添加到DispatchRuntime。MessageInspectors集合。

之后你会看到下面的例子:

  • 实现自定义IDispatchMessageInspector
  • 实现自定义IServiceBehavior,将检查器添加到运行时
  • 通过.config文件配置行为。

这应该足够让你开始了。否则请随时询问:)

如果你只是想从你的服务中获取头信息,你可以试试OperationContext.Current.IncomingMessageHeaders

相关文章:
  • 没有找到相关文章