如何解决WCF在多个客户端之间共享IClientMessageInspector实例
| 通过以下标题为“ Centralized cookie management”(位于此处的http://megakemp.com/2009/02/06/managing-shared-cookies)标题下概述的方法进行WCF服务调用时,我正在管理共享的auth cookie。 -in-wcf / 我已经设置了自定义IClientMessageInspector
,IEndpointBehavior
,BehaviorExtensionElement
作品。我的端点行为添加了一个消息检查器,如下所示:
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在互联网上杂草丛生,可以进行依赖项注入,从而创建了IInstanceProvider
,IServiceBehavior
等。但是我不认为这就是我在这里要寻找的东西。
谁能帮助我指引正确的方向?
解决方法
您只需要扩展概念,即可将cookie存储在消息检查器本身之外,以便消息检查器的所有实例共享同一存储。
穷人的入门方法只是使用静态字段而不是实例字段。显然,如果您有多个线程,则在更新字段时需要提供并发性。如果将其外推到Cookie容器概念,然后确保与所有客户共享同一个容器,您甚至可以从中得到更多。共享容器可以通过获取客户端通道的ѭ8并向其添加属性来完成,然后您的行为会在检查消息并从中拉出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 ...
}
, 您是正确的,IInstanceProvider对您的情况没有帮助-它仅用于提供服务实例。您不需要为行为设置无参数构造函数。您需要为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();
}
}
}
, 我喜欢@carlosfigueira和@drew提供的答案,但最终我想出了一个略有不同的方法。我选择通过编程来配置IEndpointBehavior,而不是通过config。使事情变得简单得多。我将端点行为更改为存储客户端消息检查器,如下所示:
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。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。