【问题标题】:WCF shared IClientMessageInspector instance across multiple clientsWCF 跨多个客户端共享 IClientMessageInspector 实例
【发布时间】:2011-09-03 16:12:42
【问题描述】:

在通过此方法进行 WCF 服务调用时,我正在管理共享身份验证 cookie,该方法在位于此处的标题“集中 cookie 管理”下概述:http://megakemp.com/2009/02/06/managing-shared-cookies-in-wcf/

我已经设置了自定义IClientMessageInspectorIEndpointBehaviorBehaviorExtensionElement,作品。我的端点行为添加了一个消息检查器,如下所示:

public class MyEndpointBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        // yuck.. Wish I had an instance of MyClientMessageInspector
        // (which has the auth cookie already) so I could just inject that
        // instance here instead of creating a new instance
        clientRuntime.MessageInspectors.Add(new MyClientMessageInspector());
    }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
    }
    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

这一切都完美地运行,但是当您想要在多个客户端上共享 cookie 时,此解决方案就会失效。因为ApplyDispatchBehavior() 方法创建了一个新实例,所以任何其他客户端都不会获得该消息检查器实例,因此也不会获得身份验证票。

然后我想尝试创建一个自定义构造函数,我可以像这样注入实例:

MyEndpointBehavior(MyClientMessageInspector msgInspector) { ... }

但是,WCF 需要无参数构造函数。通过互联网进行筛选,WCF 具有允许依赖注入、创建IInstanceProviderIServiceBehavior 等的挂钩。但我认为这不是我要在这里寻找的。​​p>

谁能帮我指引正确的方向?

【问题讨论】:

    标签: wcf dependency-injection structuremap wcf-client endpointbehavior


    【解决方案1】:

    我喜欢@carlosfigueira 和@drew 提供的答案,但我最终想出了一个稍微不同的方法。我选择以编程方式配置我的 IEndpointBehavior,而不是通过配置。让事情变得简单多了。我更改了我的端点行为以存储我的客户端消息检查器,如下所示:

    public class MyEndpointBehavior : IEndpointBehavior
    {
        private MyClientMessageInspector_myClientMessageInspector;
    
        public MyClientMessageInspector MyClientMessageInspector
        {
            get
            {
                if (_myClientMessageInspector == null)
                {
                    _myClientMessageInspector = new MyClientMessageInspector();
                }
                return _myClientMessageInspector;
             }
        }
        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }
        public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            clientRuntime.MessageInspectors.Add(MyClientMessageInspector);
        }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
        }
        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }
    

    然后我简单的在客户端之间分享了这个行为,如下:

    var behavior = new MyEndpointBehavior();
    client1.Endpoint.Behaviors.Add(behavior);
    client2.Endpoint.Behaviors.Add(behavior);
    

    现在两个客户端将共享相同的身份验证 cookie。

    【讨论】:

      【解决方案2】:

      你是对的,IInstanceProvider 对你的情况没有帮助 - 它仅用于提供 service 实例。您的行为不需要无参数构造函数。您需要为 config 元素提供一个无参数构造函数,并且此类可以使用一些依赖注入类(见下文)来创建行为所需的适当检查器类。

      namespace ConsoleApplication4
      {
          public class MyEndpointBehavior : IEndpointBehavior
          {
              IClientMessageInspector inspector;
              public MyEndpointBehavior(IClientMessageInspector inspector)
              {
                  this.inspector = inspector;
              }
      
              public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
              {
              }
      
              public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
              {
                  clientRuntime.MessageInspectors.Add(this.inspector);
              }
      
              public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
              {
              }
      
              public void Validate(ServiceEndpoint endpoint)
              {
              }
          }
          public class MyEndpointBehaviorElement : BehaviorExtensionElement
          {
              public override Type BehaviorType
              {
                  get { return typeof(MyEndpointBehavior); }
              }
      
              protected override object CreateBehavior()
              {
                  return new MyEndpointBehavior(ClientInspectorFactory.GetClientInspector());
              }
          }
          public class MyClientInspector : IClientMessageInspector
          {
              public MyClientInspector()
              {
              }
      
              public void AfterReceiveReply(ref Message reply, object correlationState)
              {
                  Console.WriteLine("AfterReceiveReply");
              }
      
              public object BeforeSendRequest(ref Message request, IClientChannel channel)
              {
                  Console.WriteLine("BeforeSendRequest");
                  return null;
              }
          }
          public static class ClientInspectorFactory
          {
              static IClientMessageInspector instance;
              public static IClientMessageInspector GetClientInspector()
              {
                  if (instance == null)
                  {
                      instance = new MyClientInspector();
                  }
      
                  return instance;
              }
          }
          [ServiceContract]
          public interface ITest
          {
              [OperationContract]
              int Add(int x, int y);
          }
          public class Service : ITest
          {
              public int Add(int x, int y) { return x + y; }
          }
          class Program
          {
              static void Main(string[] args)
              {
                  ServiceHost host = new ServiceHost(typeof(Service));
                  host.Open();
                  Console.WriteLine("Host opened");
      
                  ChannelFactory<ITest> factory = new ChannelFactory<ITest>("client1");
                  ITest proxy = factory.CreateChannel();
                  Console.WriteLine(proxy.Add(3, 4));
                  ((IClientChannel)proxy).Close();
                  factory.Close();
      
                  factory = new ChannelFactory<ITest>("client2");
                  proxy = factory.CreateChannel();
                  Console.WriteLine(proxy.Add(5, 8));
                  ((IClientChannel)proxy).Close();
                  factory.Close();
      
                  host.Close();
              }
          }
      }
      

      【讨论】:

      • 我喜欢你的回答,似乎正是我需要的。虽然,我最终采用了不同的方法,因为我没有引用“ClientInspectorFactory”。我的所有依赖项都通过我的 IoC 容器 - StructureMap 注入。我希望有一种方法可以通过构造函数使用注入,但它看起来不像是一种方法。无论如何,我提高了你的答案。
      【解决方案3】:

      您只需要扩展这个概念,以便将 cookie 存储在消息检查器本身之外,以便消息检查器的所有实例共享相同的存储空间。

      穷人的方法,刚开始,就是使用静态字段而不是实例字段。显然,如果您有多个线程,则需要在更新字段时提供并发性。如果您将其推断为 cookie 容器概念,然后确保您与所有客户端共享同一个容器,那么您可以从那里变得更加出色。可以通过获取客户端通道的ChannelParameterCollection 并向其添加属性来共享容器,然后您的行为会在检查消息并从中提取 cookie 时查找该属性。看起来有点像这样:

      应用逻辑

      // Hold onto a static cookie container
      public static CookieContainer MyCookieContainer;
      
      // When instantiating the client add the cookie container to the channel parameters
      MyClient client = new MyClient();
      client.InnerChannel.GetProperty<ChannelParameterCollection>().Add(MyCookieContainer);
      

      消息检查器逻辑

      public void BeforeSendMessage(ref Message, IClientChannel clientChannel)
      {
          // Find the cookie container for the current channel
          CookieContainer cookieContainer = clientChannel.GetProperty<ChannelParameterCollection>().Select(p => p as CookieContainer).Where(cc => cc != null).First();
      
          // ... use the cookie container to set header on outgoing context ...
      }
      

      【讨论】:

      • 感谢您的回答。我不想管理 CookieContainer 对象,但您的回答让我更深入地了解了我最终如何让我的解决方案发挥作用(请参阅我的回答)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-30
      • 1970-01-01
      相关资源
      最近更新 更多