参考资料:
http://www.cnblogs.com/czcz1024/p/3333138.html
http://megakemp.com/2009/02/06/managing-shared-cookies-in-wcf/
首先使用的绑定 必须允许Cookie传播
<wsHttpBinding> <binding closeTimeout="00:01:30" openTimeout="00:01:30" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transactionFlow="true" allowCookies="True"> <reliableSession enabled="true" /> <security mode= "None"> </security> </binding> </wsHttpBinding>
其次
服务需要基于ASP.NET 的激活进行host
<!--运行服务以ASP.NET激活模式激活服务--> <serviceHostingEnvironment aspNetCompatibilityEnabled ="true" multipleSiteBindingsEnabled="true" />
第三 服务的实现 ,基于属性的声明允许以ASP.NET的方式访问
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class AddressWCFService : IAddressWCFService
第四 最关键的一部,实现客户端的消息发送 响应拦截器
/// <summary> /// Gets the singleton <see cref="ClientIdentityMessageInspector" /> instance. /// </summary> public static CookieManagerMessageInspector Instance { get { if (instance == null) { instance = new CookieManagerMessageInspector(); } return instance; } } /// <summary> /// Inspects 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) { HttpResponseMessageProperty httpResponse = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; if (httpResponse != null) { string cookie = httpResponse.Headers[HttpResponseHeader.SetCookie]; if (!string.IsNullOrEmpty(cookie)) { this.sharedCookie = cookie; } } } /// <summary> /// Inspects 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 client object channel.</param> /// <returns> /// <strong>Null</strong> since no message correlation is used. /// </returns> public object BeforeSendRequest(ref Message request, IClientChannel channel) { HttpRequestMessageProperty httpRequest; // The HTTP request object is made available in the outgoing message only when // the Visual Studio Debugger is attacched to the running process if (!request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { request.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty()); } httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]; httpRequest.Headers.Add(HttpRequestHeader.Cookie, this.sharedCookie); return null; }
最后一步:为客户端的Behavior添加行为扩展
public class CookieManagerEndpointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { return; } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(CookieManagerMessageInspector.Instance); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { return; } public void Validate(ServiceEndpoint endpoint) { return; } }
到此 就可以配置客户端ClientProxy代理的实例 进行Cookie的传播共享了。下面是客户端的Behavior的扩展
<behaviors> <endpointBehaviors> <behavior name="CookieBehaviorConfig"> <clear/> <!--注册行为扩展--> <CookieBehavior /> </behavior> </endpointBehaviors> </behaviors> <!--扩展 客户端的Behavior--> <extensions> <behaviorExtensions> <add name="CookieBehavior" type="Smart.Client.CookieManager.CookieManagerBehaviorExtension, Smart.Client.CookieManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions>
以下是作者原文,本文基于作者思路而来,谢谢。
wcf读写cookie
一般来说,web应用的服务端(aspx或mvc的action)调用wcf时,是一个服务与服务的通讯,而不是客户端(浏览器)与服务器的通讯。
这种情况下,如果要在wcf端处理客户端的cookie,就需要做一些额外的开发。
首先,在wcf的web.config里,需要启用
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
之后,wcf的实现类需要加attribute
[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Required)]
做完上面的两步,在wcf具体的方法里就可以使用HttpContext.Current .Resposne/.Request来读写cookie了。
但是,正如上面所说,这是一个服务器与服务器的通讯,客户端(web应用)并不会自动发送cookie到wcf。所以客户端还得做更多的工作
核心在IClientMessageInspector 这个接口,他有
BeforeSendRequest和AfterReceiveReply两个方法。
我们的目的是在beforeSendRequest时,在请求中加入cookie信息,在AfterReciveReply方法中,从响应中获取要设置的cookie,并真正设置到客户端(浏览器)
public class CookieMessageInspector : IClientMessageInspector
新建一个类,来实现接口
1: public object BeforeSendRequest(ref Message request,System.ServiceModel.IClientChannel channel)
2: {
3: var cookie = GetCookieValue();
4:
5: HttpRequestMessageProperty httpRequestMessage;
6: object httpRequestMessageObject;
7:
8: if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
9: {
10: httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
11: if (string.IsNullOrEmpty(httpRequestMessage.Headers["Cookie"]))
12: {
13: httpRequestMessage.Headers["Cookie"] = cookie;
14: }
15: }
16: else
17: {
18: httpRequestMessage = new HttpRequestMessageProperty();
19: httpRequestMessage.Headers.Add("Cookie", cookie);
20: request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
21: }
22:
23: return null;
24: }
我们需要把cookie值,转化成字符串进行传递,cookie名=cookie值这样的形式,多个cookie用分号(;)分隔。
我们新建的这个类,要如何使用呢,他需要在一个behavior中调用。所以我们还得建立一个behavior
public class CookieBehavior : IEndpointBehavior
实现接口IEndpointBehavior,并且在方法ApplyClientBehavior中加入我们刚才的类
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint,ClientRuntime behavior) { behavior.MessageInspectors.Add(new CookieMessageInspector()); }
其他需要实现的方法,直接return即可
通过vs添加服务引用,他会自动生成一个代理类。在new这个代理类之后,加入我们新建的behavior
var client = new TestSvc.TestSvcClient(); CookieBehavior b = new CookieBehavior(); client.Endpoint.Behaviors.Add(b);
之后,我们调用方法,就会把cookie信息传递到wcf了。
到目前为止,我们可以解决web向wcf传递当前cookie,wcf可以读取cookie。
下面我们解决在wcf中设置cookie。
同样的原理,在读取的时候,我们是在BeforeSendRequest中实现的,那写入则需要在AfterReceiveReply中实现了。
在wcf中使用
HttpContext.Current.Response.Cookies.Add(new HttpCookie("test123", "123"));
类似这样的代码设置cookie,他在响应的header里会有Set-Cookie,我们只需要处理Set-Cookie里面的内容就可以了。
在写入多个cookie时,与读操作不同,这里是用逗号(,)分隔的,并且默认他会带cookie的path,如果你设置了cookie的domain,过期时间等,他也会传递,并且同一个cookie的若干属性之间是用分号(;)分隔的
1: public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply,object correlationState)
2: {
3: if (reply.Properties.ContainsKey("httpResponse"))
4: {
5: HttpResponseMessageProperty httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
6:
7: if (httpResponse != null)
8: {
9: string cookie = httpResponse.Headers.Get("Set-Cookie");
10: if (!string.IsNullOrEmpty(cookie))
11: {
12: //解析并设置cookie
13: }
14: }
15: }
16: }
具体如何解析,就是字符串操作,就不细说了。
以上方式可以实现web应用的服务器端与wcf通讯时,附带客户端(浏览器)的cookie信息,进行读写操作。
但是每次都需要对client进行设置,相对比较麻烦。我们可以通过web.config中的配置来让client自动加入behavior。
同样,我们需要下面这样的一个类
public class CookieBehaviorExtensionElement :BehaviorExtensionElement { protected override object CreateBehavior() { return new CookieBehavior(); } public override Type BehaviorType { get { return typeof(CookieBehavior); } } }
之后,在web项目的web.config中
<system.serviceModel>
此节点下,首先需要加入
<extensions> <behaviorExtensions> <add name="CookieBehavior" type="webhost.CookieBehaviorExtensionElement, webhost, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions>
注意type是的写法,需要根据自己项目的命名空间来进行修改
之后
<behaviors> <endpointBehaviors> <behavior name="CookieBehaviorConfig"> <CookieBehavior /> </behavior> </endpointBehaviors> </behaviors>
黄色部分,为extensions中我们添加的那条的name
最后
<endpoint address="http://localhost:5351/testSvc.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITestSvc" contract="TestSvc.ITestSvc" name="BasicHttpBinding_ITestSvc" behaviorConfiguration="CookieBehaviorConfig" />
设置endpoint的behaviorConfiguration为behaviors节中添加的behavior的name
如此,我们就可以不用每次new client之后在去设置它的behavior了