WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
2015-07-10 14:22
651 查看
原文: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从这里下载)
[/code]
步骤二、实现服务:CalculatorService
服务的实现和.svc都定义在一个ASP.NET Web站点项目中。对于定义在 CalculatorService中的每次运算,先通过HttpContext从SessionState中取出上一次运算的结果,完成运算后再将新的运算结果保存到SessionState中。通过在CalculatorService上应用AspNetCompatibilityRequirementsAttribute实现对ASP.NET兼容模式的支持。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[/code]
下面是CalculatorService对应的.svc的定义和Web.config。为了简洁,在<@ServiceHost%>指令中,仅仅设置一个必需属性Service。对于ASP.NET兼容模式的支持,配置<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>必不可少。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[/code]
步骤三、创建客户端:Client
CalculatorService的客户端应用通过一个Console应用程序模拟,其服务调用方式并无特别之处,下面是相关的代码和配置。
[/code]
[/code]
但是,但我们运行客户端的程序,输出的结果并不像我们希望的那样。从下面的结果可以看出,每次通过GetResult()方法得到的结果都是0,也就是说,服务端并没有将运算结果保存下来。
[/code]
允许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,该属性可以通过配置进行设置。为此,我们对客户端的配置进行了如下的修改。再次运行我们的案例程序,将会得到你期望的输出。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[/code]
客户端输出结果:
[/code]
由于在ASP.NET兼容模式下,ASP.NET采用与.aspx Page完全一样的方式处理基于.svc的请求,换言之,我们就可以借助当前HttpContext的SessionState维护会话状态,进而创建一个支持会话的WCF Service。接下来,我们就通过一个简单的例子,一步步地创建这样的会话服务。本案例采用如图1所示的3层结构。 (Source Code从这里下载)
[code]using System.ServiceModel;
namespace Artech.AspCompatibleServices.Contracts
{
[ServiceContract]
public interface ICalculator
{
[OperationContract]
void Add(double x);
[OperationContract]
void Subtract(double x);
[OperationContract]
void Multiply(double x);
[OperationContract]
void Divide(double x);
[OperationContract]
double GetResult();
}
}
[/code]
步骤二、实现服务:CalculatorService
服务的实现和.svc都定义在一个ASP.NET Web站点项目中。对于定义在 CalculatorService中的每次运算,先通过HttpContext从SessionState中取出上一次运算的结果,完成运算后再将新的运算结果保存到SessionState中。通过在CalculatorService上应用AspNetCompatibilityRequirementsAttribute实现对ASP.NET兼容模式的支持。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[code] using System.ServiceModel.Activation;
using System.Web;
using Artech.AspCompatibleServices.Contracts;
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CalculatorService : ICalculator
{
public void Add(double x)
{
HttpContext.Current.Session["__Result"] = GetResult() + x;
}
public void Subtract(double x)
{
HttpContext.Current.Session["__Result"] = GetResult() - x;
}
public void Multiply(double x)
{
HttpContext.Current.Session["__Result"] = GetResult() * x;
}
public void Divide(double x)
{
HttpContext.Current.Session["__Result"] = GetResult() / x;
}
public double GetResult()
{
if (HttpContext.Current.Session["__Result"] == null)
{
HttpContext.Current.Session["__Result"] = 0.0;
}
return (double)HttpContext.Current.Session["__Result"];
}
}
[/code]
下面是CalculatorService对应的.svc的定义和Web.config。为了简洁,在<@ServiceHost%>指令中,仅仅设置一个必需属性Service。对于ASP.NET兼容模式的支持,配置<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>必不可少。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[code] <?xml version="1.0"?>
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<services>
<service name="CalculatorService">
<endpoint binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator" />
</service>
</services>
</system.serviceModel>
</configuration>
[/code]
步骤三、创建客户端:Client
CalculatorService的客户端应用通过一个Console应用程序模拟,其服务调用方式并无特别之处,下面是相关的代码和配置。
[code] using System;
using System.ServiceModel;
using Artech.AspCompatibleServices.Contracts;
namespace Artech.AspCompatibleServices.Clients
{
class Program
{
static void Main(string[] args)
{
using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("CalculatorService"))
{
ICalculator proxy = channelFactory.CreateChannel();
Console.WriteLine("初始值为:{0}", proxy.GetResult()); proxy.Add(1);
Console.WriteLine("Add(3)", proxy.GetResult());
Console.WriteLine("运算结果为:{0}", proxy.GetResult()); proxy.Multiply(10);
Console.WriteLine("Multiply(10)", proxy.GetResult()); Console.WriteLine("运算结果为:{0}", proxy.GetResult()); proxy.Subtract(2);
Console.WriteLine("Subtract(2)", proxy.GetResult()); Console.WriteLine("运算结果为:{0}", proxy.GetResult());
} Console.Read();
}
}
}
[/code]
[code] <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc"
binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator"
name="CalculatorService"/>
</client> </system.serviceModel>
</configuration>
[/code]
但是,但我们运行客户端的程序,输出的结果并不像我们希望的那样。从下面的结果可以看出,每次通过GetResult()方法得到的结果都是0,也就是说,服务端并没有将运算结果保存下来。
[code] 初始值为:0
Add(3)运算结果为:0
Multiply(10)运算结果为:0
Subtract(2)运算结果为:0
[/code]
允许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,该属性可以通过配置进行设置。为此,我们对客户端的配置进行了如下的修改。再次运行我们的案例程序,将会得到你期望的输出。
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre{ margin: 0em;}
.csharpcode .rem{ color: #008000;}
.csharpcode .kwrd{ color: #0000ff;}
.csharpcode .str{ color: #006080;}
.csharpcode .op{ color: #0000c0;}
.csharpcode .preproc{ color: #cc6633;}
.csharpcode .asp{ background-color: #ffff00;}
.csharpcode .html{ color: #800000;}
.csharpcode .attr{ color: #ff0000;}
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum{ color: #606060;}
[code] <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost/AspCompatibleServices/CalculatorService.svc"
binding="wsHttpBinding" contract="Artech.AspCompatibleServices.Contracts.ICalculator"
name="CalculatorService" bindingConfiguration="CookieAllowableBinding"/>
</client>
<bindings>
<wsHttpBinding>
<binding name="CookieAllowableBinding" allowCookies="true"/>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
[/code]
客户端输出结果:
[code] 初始值为:0
Add(3)运算结果为:3
Multiply(10)运算结果为:30
Subtract(2)运算结果为:28
[/code]
相关文章推荐
- asp.net 去重复验证
- 【Metasploit魔鬼训练营--实践笔记】4.2.3 SQL 注入漏洞探测
- ASP.NET性能优化之减少请求
- asp.net 接入微信公众平台,回复消息,解决乱码问题
- WCF技术剖析之二:再谈IIS与ASP.NET管道
- WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构
- ASP.NET控件组(lable1、label2、label3.....labeln)的赋值
- asp.net实现三层架构的例子
- 介绍“Razor”— ASP.NET的一个新视图引擎
- [每日刷题(2015/7/10)]简述ASP.NET的页面运行机制
- ASP.NET操作Word的IIS权限配置
- ASP.NET简单实现注销功能
- 怎样学好ASP.NET技术
- ASP.NET | WebForm 处理机
- ASP.NET简单实现注销功能
- asp.net实现三层架构的例子
- SharePoint 2013 表单认证使用ASP.Net配置工具添加用户
- spring mvc + JasperReports
- Asp.net mvc5 系列笔记
- SharePoint 2013 表单认证使用ASP.Net配置工具添加用户