WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
在《基于IIS的WCF服務(wù)寄宿(Hosting)實(shí)現(xiàn)揭秘》中,我們談到在采用基于IIS(或者說(shuō)基于ASP.NET)的WCF服務(wù)寄宿中,具有兩種截然不同的運(yùn)行模式:ASP.NET并行(Side by Side)模式和ASP.NET兼容模式。對(duì)于前者,WCF通過(guò)HttpModule實(shí)現(xiàn)了服務(wù)的寄宿,而對(duì)于后者,WCF的服務(wù)寄宿通過(guò)一個(gè)HttpHandler實(shí)現(xiàn)。只有在ASP.NET兼容模式下,我們熟悉的一些ASP.NET機(jī)制才能被我們使用,比如通過(guò)HttpContext的請(qǐng)求下下文;基于文件或者Url的授權(quán);HttpModule擴(kuò)展;身份模擬(Impersonation)等。
由于在ASP.NET兼容模式下,ASP.NET采用與.aspx Page完全一樣的方式處理基于.svc的請(qǐng)求,換言之,我們就可以借助當(dāng)前HttpContext的SessionState維護(hù)會(huì)話狀態(tài),進(jìn)而創(chuàng)建一個(gè)支持會(huì)話的WCF Service。接下來(lái),我們就通過(guò)一個(gè)簡(jiǎn)單的例子,一步步地創(chuàng)建這樣的會(huì)話服務(wù)。本案例采用如圖1所示的3層結(jié)構(gòu)。 (Source Code從這里下載)
? 圖1 ASP.NET兼容模式案例應(yīng)用結(jié)構(gòu)?
步驟一、定義服務(wù)契約:ICalculator
案例依然沿用計(jì)算服務(wù)的例子,不過(guò)通過(guò)原來(lái)直接與傳入操作數(shù)并得到運(yùn)算結(jié)果的方式不同,為了體現(xiàn)會(huì)話狀態(tài)的存在,我們將本案例的WCF服務(wù)定義成“累積計(jì)算服務(wù)”:保留上一次運(yùn)算的結(jié)果,并將其作為后續(xù)運(yùn)算的操作數(shù)。為此,定義了如下一個(gè)接口作為服務(wù)契約:前面4個(gè)操作代表基本的加、減、乘、除運(yùn)算,計(jì)算結(jié)果通過(guò)GetResult方法獲得。
1: using System.ServiceModel; 2: namespace Artech.AspCompatibleServices.Contracts 3: { 4: [ServiceContract] 5: public interface ICalculator 6: { 7: [OperationContract] 8: void Add(double x); 9: [OperationContract] 10: void Subtract(double x); 11: [OperationContract] 12: void Multiply(double x); 13: [OperationContract] 14: void Divide(double x); 15: [OperationContract] 16: double GetResult(); 17: } 18: }步驟二、實(shí)現(xiàn)服務(wù):CalculatorService
服務(wù)的實(shí)現(xiàn)和.svc都定義在一個(gè)ASP.NET Web站點(diǎn)項(xiàng)目中。對(duì)于定義在 CalculatorService中的每次運(yùn)算,先通過(guò)HttpContext從SessionState中取出上一次運(yùn)算的結(jié)果,完成運(yùn)算后再將新的運(yùn)算結(jié)果保存到SessionState中。通過(guò)在CalculatorService上應(yīng)用AspNetCompatibilityRequirementsAttribute實(shí)現(xiàn)對(duì)ASP.NET兼容模式的支持。
1: using System.ServiceModel.Activation; 2: using System.Web; 3: using Artech.AspCompatibleServices.Contracts; 4: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 5: public class CalculatorService : ICalculator 6: { 7: public void Add(double x) 8: { 9: HttpContext.Current.Session["__Result"] = GetResult() + x; 10: } 11: public void Subtract(double x) 12: { 13: HttpContext.Current.Session["__Result"] = GetResult() - x; 14: } 15: public void Multiply(double x) 16: { 17: HttpContext.Current.Session["__Result"] = GetResult() * x; 18: } 19: public void Divide(double x) 20: { 21: HttpContext.Current.Session["__Result"] = GetResult() / x; 22: } 23: public double GetResult() 24: { 25: if (HttpContext.Current.Session["__Result"] == null) 26: { 27: HttpContext.Current.Session["__Result"] = 0.0; 28: } 29: return (double)HttpContext.Current.Session["__Result"]; 30: } 31: }下面是CalculatorService對(duì)應(yīng)的.svc的定義和Web.config。為了簡(jiǎn)潔,在<@ServiceHost%>指令中,僅僅設(shè)置一個(gè)必需屬性Service。對(duì)于ASP.NET兼容模式的支持,配置<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>必不可少。
1: <?xml version="1.0"?> 2: <configuration> 3: <system.serviceModel> 4: <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> 5: <services> 6: <service name="CalculatorService"> 7: <endpoint binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" /> 8: </service> 9: </services> 10: </system.serviceModel> 11: </configuration>步驟三、創(chuàng)建客戶端:Client
CalculatorService的客戶端應(yīng)用通過(guò)一個(gè)Console應(yīng)用程序模擬,其服務(wù)調(diào)用方式并無(wú)特別之處,下面是相關(guān)的代碼和配置。
1: using System; 2: using System.ServiceModel; 3: using Artech.AspCompatibleServices.Contracts; 4: namespace Artech.AspCompatibleServices.Clients 5: { 6: class Program 7: { 8: static void Main(string[] args) 9: { 10: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("CalculatorService")) 11: { 12: ICalculator proxy = channelFactory.CreateChannel(); 13: Console.WriteLine("初始值為: {0}", proxy.GetResult()); proxy.Add(1); 14: Console.WriteLine("Add(3)", proxy.GetResult()); 15: Console.WriteLine("運(yùn)算結(jié)果為: {0}", proxy.GetResult()); proxy.Multiply(10); 16: Console.WriteLine("Multiply(10)", proxy.GetResult()); Console.WriteLine("運(yùn)算結(jié)果為: {0}", proxy.GetResult()); proxy.Subtract(2); 17: Console.WriteLine("Subtract(2)", proxy.GetResult()); Console.WriteLine("運(yùn)算結(jié)果為: {0}", proxy.GetResult()); 18: } Console.Read(); 19: } 20: } 21: }?
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <client> 5: <endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc" 6: binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" 7: name="CalculatorService"/> 8: </client> </system.serviceModel> 9: </configuration>但是,但我們運(yùn)行客戶端的程序,輸出的結(jié)果并不像我們希望的那樣。從下面的結(jié)果可以看出,每次通過(guò)GetResult()方法得到的結(jié)果都是0,也就是說(shuō),服務(wù)端并沒(méi)有將運(yùn)算結(jié)果保存下來(lái)。
1: 初始值為:0 2: Add(3)運(yùn)算結(jié)果為:0 3: Multiply(10)運(yùn)算結(jié)果為:0 4: Subtract(2)運(yùn)算結(jié)果為:0允許Cookie傳遞
要解釋這個(gè)問(wèn)題,得從Session的實(shí)現(xiàn)機(jī)制說(shuō)起。眾所周知,HTTP是無(wú)狀態(tài)(Stateless)的傳輸協(xié)議,對(duì)服務(wù)端來(lái)說(shuō),它收到的每個(gè)HTTP請(qǐng)求都是全新的請(qǐng)求。ASP.NET會(huì)話(Session)的實(shí)現(xiàn)很簡(jiǎn)單,就是讓每次HTTP請(qǐng)求攜帶Session的識(shí)別信息(Session ID),那么服務(wù)就可以根據(jù)此信息判斷請(qǐng)求來(lái)自哪個(gè)客戶端了。關(guān)于Session識(shí)別信息的保存,ASP.NET有兩種方式:Cookie和URL,前者將其放到Cookie中,每次HTTP請(qǐng)求將會(huì)攜帶該Cookie的值,后者則將其作為請(qǐng)求URL的一部分。一般情況下采用基于Cookie的實(shí)現(xiàn)機(jī)制,如果Cookie禁用則采用后者。
那么對(duì)于ASP.NET兼容模式下的WCF也一樣,要想讓服務(wù)端能夠識(shí)別會(huì)話,就需要讓每個(gè)服務(wù)調(diào)用的HTTP請(qǐng)求攜帶Session的識(shí)別信息,我們也可以通過(guò)傳遞Cookie的方式來(lái)解決這個(gè)問(wèn)題。對(duì)于WCF來(lái)說(shuō),Cookie傳遞能夠通過(guò)Binding來(lái)控制,對(duì)于WsHttpBinding來(lái)說(shuō),默認(rèn)情況下并不允許Cookie的傳遞。我們可以通過(guò)WsHttpBinding的AllowCookies來(lái)控制是否允許傳遞Cookie,該屬性可以通過(guò)配置進(jìn)行設(shè)置。為此,我們對(duì)客戶端的配置進(jìn)行了如下的修改。再次運(yùn)行我們的案例程序,將會(huì)得到你期望的輸出。
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <client> 5: <endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc" 6: binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" 7: name="CalculatorService" bindingConfiguration="CookieAllowableBinding"/> 8: </client> 9: <bindings> 10: <wsHttpBinding> 11: <binding name="CookieAllowableBinding" allowCookies="true"/> 12: </wsHttpBinding> 13: </bindings> 14: </system.serviceModel> 15: </configuration>客戶端輸出結(jié)果:
1: 初始值為:0 2: Add(3)運(yùn)算結(jié)果為:3 3: Multiply(10)運(yùn)算結(jié)果為:30 4: Subtract(2)運(yùn)算結(jié)果為:28 作者:Artech出處:http://artech.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
總結(jié)
以上是生活随笔為你收集整理的WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 打造基于大并发通信技术及大数据技术的O2
- 下一篇: Linux-sudo详解