WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务

简介:

在《基于IIS的WCF服务寄宿(Hosting)实现揭秘》中,我们谈到在采用基于IIS(或者说基于ASP.NET)的WCF服务寄宿中,具有两种截然不同的运行模式:ASP.NET并行(Side by Side)模式和ASP.NET兼容模式。对于前者,WCF通过HttpModule实现了服务的寄宿,而对于后者,WCF的服务寄宿通过一个HttpHandler实现。只有在ASP.NET兼容模式下,我们熟悉的一些ASP.NET机制才能被我们使用,比如通过HttpContext的请求下下文;基于文件或者Url的授权;HttpModule扩展;身份模拟(Impersonation)等。

由于在ASP.NET兼容模式下,ASP.NET采用与.aspx Page完全一样的方式处理基于.svc的请求,换言之,我们就可以借助当前HttpContext的SessionState维护会话状态,进而创建一个支持会话的WCF Service。接下来,我们就通过一个简单的例子,一步步地创建这样的会话服务。本案例采用如图1所示的3层结构。 (Source Code从这里下载)

clip_image002 
图1 ASP.NET兼容模式案例应用结构

 

步骤一、定义服务契约:ICalculator

案例依然沿用计算服务的例子,不过通过原来直接与传入操作数并得到运算结果的方式不同,为了体现会话状态的存在,我们将本案例的WCF服务定义成“累积计算服务”:保留上一次运算的结果,并将其作为后续运算的操作数。为此,定义了如下一个接口作为服务契约:前面4个操作代表基本的加、减、乘、除运算,计算结果通过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: }

步骤二、实现服务:CalculatorService

服务的实现和.svc都定义在一个ASP.NET Web站点项目中。对于定义在 CalculatorService中的每次运算,先通过HttpContext从SessionState中取出上一次运算的结果,完成运算后再将新的运算结果保存到SessionState中。通过在CalculatorService上应用AspNetCompatibilityRequirementsAttribute实现对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对应的.svc的定义和Web.config。为了简洁,在<@ServiceHost%>指令中,仅仅设置一个必需属性Service。对于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> 

步骤三、创建客户端:Client

CalculatorService的客户端应用通过一个Console应用程序模拟,其服务调用方式并无特别之处,下面是相关的代码和配置。

   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("运算结果为: {0}", proxy.GetResult()); proxy.Multiply(10);
  16:                 Console.WriteLine("Multiply(10)", proxy.GetResult()); Console.WriteLine("运算结果为: {0}", proxy.GetResult()); proxy.Subtract(2); 
  17:                 Console.WriteLine("Subtract(2)", proxy.GetResult()); Console.WriteLine("运算结果为: {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>

但是,但我们运行客户端的程序,输出的结果并不像我们希望的那样。从下面的结果可以看出,每次通过GetResult()方法得到的结果都是0,也就是说,服务端并没有将运算结果保存下来。

   1: 初始值为:0
   2: Add(3)运算结果为:0
   3: Multiply(10)运算结果为:0
   4: Subtract(2)运算结果为:0

允许Cookie传递

要解释这个问题,得从Session的实现机制说起。众所周知,HTTP是无状态(Stateless)的传输协议,对服务端来说,它收到的每个HTTP请求都是全新的请求。ASP.NET会话(Session)的实现很简单,就是让每次HTTP请求携带Session的识别信息(Session ID),那么服务就可以根据此信息判断请求来自哪个客户端了。关于Session识别信息的保存,ASP.NET有两种方式:Cookie和URL,前者将其放到Cookie中,每次HTTP请求将会携带该Cookie的值,后者则将其作为请求URL的一部分。一般情况下采用基于Cookie的实现机制,如果Cookie禁用则采用后者。

那么对于ASP.NET兼容模式下的WCF也一样,要想让服务端能够识别会话,就需要让每个服务调用的HTTP请求携带Session的识别信息,我们也可以通过传递Cookie的方式来解决这个问题。对于WCF来说,Cookie传递能够通过Binding来控制,对于WsHttpBinding来说,默认情况下并不允许Cookie的传递。我们可以通过WsHttpBinding的AllowCookies来控制是否允许传递Cookie,该属性可以通过配置进行设置。为此,我们对客户端的配置进行了如下的修改。再次运行我们的案例程序,将会得到你期望的输出。

   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>

客户端输出结果:

   1: 初始值为:0
   2: Add(3)运算结果为:3
   3: Multiply(10)运算结果为:30
   4: Subtract(2)运算结果为:28

作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
21天前
|
存储 NoSQL MongoDB
.NET MongoDB数据仓储和工作单元模式封装
.NET MongoDB数据仓储和工作单元模式封装
45 15
|
19天前
|
开发框架 算法 .NET
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
C#/.NET/.NET Core技术前沿周刊 | 第 15 期(2024年11.25-11.30)
|
19天前
|
开发框架 Cloud Native .NET
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
C#/.NET/.NET Core技术前沿周刊 | 第 16 期(2024年12.01-12.08)
|
2月前
|
自然语言处理 物联网 图形学
.NET 技术凭借其独特的优势和特性,为开发者们提供了一种高效、可靠且富有创造力的开发体验
本文深入探讨了.NET技术的独特优势及其在多个领域的应用,包括企业级应用、Web应用、桌面应用、移动应用和游戏开发。通过强大的工具集、高效的代码管理、跨平台支持及稳定的性能,.NET为开发者提供了高效、可靠的开发体验,并面对技术更新和竞争压力,不断创新发展。
101 7
|
2月前
|
开发框架 安全 .NET
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱
在数字化时代,.NET 技术凭借跨平台兼容性、丰富的开发工具和框架、高效的性能及强大的安全稳定性,成为软件开发的重要支柱。它不仅加速了应用开发进程,提升了开发质量和可靠性,还促进了创新和业务发展,培养了专业人才和技术社区,为软件开发和数字化转型做出了重要贡献。
45 5
|
2月前
|
传感器 人工智能 供应链
.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。
本文深入探讨了.NET开发技术在数字化时代的创新作用,从高效的开发环境、强大的性能表现、丰富的库和框架资源等方面揭示了其关键优势。通过企业级应用、Web应用及移动应用的创新案例,展示了.NET在各领域的广泛应用和巨大潜力。展望未来,.NET将与新兴技术深度融合,拓展跨平台开发,推动云原生应用发展,持续创新。
50 4
|
2月前
|
开发框架 .NET C#
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位
.NET 技术凭借高效开发环境、强大框架支持及跨平台特性,在软件开发中占据重要地位。从企业应用到电子商务,再到移动开发,.NET 均展现出卓越性能,助力开发者提升效率与项目质量,推动行业持续发展。
40 4
|
2月前
|
机器学习/深度学习 人工智能 物联网
.NET 技术:引领未来开发潮流
.NET 技术以其跨平台兼容性、高效的开发体验、强大的性能表现和安全可靠的架构,成为引领未来开发潮流的重要力量。本文深入探讨了 .NET 的核心优势与特点,及其在企业级应用、移动开发、云计算、人工智能等领域的广泛应用,展示了其卓越的应用价值和未来发展前景。
70 5
|
2月前
|
机器学习/深度学习 人工智能 Cloud Native
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台
在数字化时代,.NET 技术凭借其跨平台兼容性、丰富的类库和工具集以及卓越的性能与效率,成为软件开发的重要平台。本文深入解析 .NET 的核心优势,探讨其在企业级应用、Web 开发及移动应用等领域的应用案例,并展望未来在人工智能、云原生等方面的发展趋势。
48 3
|
2月前
|
敏捷开发 缓存 中间件
.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素
本文深入探讨了.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素,并通过企业级应用和Web应用开发的实践案例,展示了如何在实际项目中应用这些模式,旨在为开发者提供有益的参考和指导。
47 3