REST 架构风格下的WCF特性简介

  REST(Representational State Transfer)作为一种优秀的架构风格,自诞生以来越来越受广大开发者的青睐。对没有接触过REST的开发人员,可以参阅本人的上一篇博客:对REST架构的理解及Jquery+JSON+RESTful WCF 

由于REST推崇的简易型,以及基于HTTP协议的特点,它又有一些什么样的特性呢。?本问将围绕这个主题分两部分展开,并结合Demo程序加以介绍。第一部分介绍涉及到的知识点,第二部分介绍Demo。


  主要涉及到的知识点如下:
  1、绑定协议与行为(webHttpBinding 与webHttpBehavior)
  2、Action的定义(POST、GET、DELETE、PUT)
  3、URI(Uniform Resource Identifier)的指定
  4、webServiceHost与webServiceHostFactory

*1、绑定协议与行为(webHttpBinding 与webHttpBehavior)
众所周知,WCF中支持的协议有很多,如wsHttpBinding等ws-*系列的、netTcpBinding、支持MSMQ的系列协议等。在构建REST架构风格的WCF中
我们使用的协议为webHttpBinding 。与之相对应的行为则是webHttpBehavior。

MSDN对他们的的描述分别为:
webHttpBinding:一个绑定,可用于为通过 HTTP 请求(而不是 SOAP 消息)公开的 Windows Communication Foundation (WCF) Web 服务配置终结点。
webHttpBehavior:启用 Windows Communication Foundation (WCF) 服务的 Web 编程模型。
WebHttpBehavior 行为与 WebHttpBinding 绑定一起使用时,支持 WCF 公开和访问 Web 样式服务。WebServiceHost 会自动将此行为添加到使用 WebHttpBinding 的终结点。
webHttpBinding作为REST 架构风格下的WCF 所使用的协议,它和其他协议一样通知WCF如何为通讯建立通道堆栈,也就是建立相应的Channel以用于
通讯。webHttpBehavior的作用是为REST WCF的端点提供行为的配置,它决定了WebHttpDispatchOperationSelector如何决定选择何种路由方式
来访问资源。
*2、Action中的各个动词决定了对资源进行何种操作。
POST:对客户端知晓的资源进行添加。它是WebInvokeAttribute默认的操作动作
Get:对资源进行获取。它是WebGetAttribute默认的操作动作
DELETE:对资源进行删除。一般由WebInvokeAttribute指定
PUT:对资源进行添加或者修改。一般由WebInvokeAttribute指定

*3、URI
URI(Uniform Resource Identifier)的指定是通过REST WCF编程模型中的WebGetAttribute、 WebInvokeAttribute两种特性来标识的。它通过将
WebGetAttribute、 WebInvokeAttribute两种特性的UriTemplate属性指定。通常将它指定在服务契约对应的接口上,WebHttpDispathOperationSelector通过URI来决定
对资源进行操作。示例定义如下:
[OperationContract]
[WebGet(UriTemplate = "/")]
List<LogEntity> GetAll();

*4、webServiceHost与webServiceHostFactory
对WCF有些了解的人都知道,它的配置是比较复杂的。REST所推崇的是简易型,因此在REST WCF编程模型中,MS推出了webServiceHost
与webServiceHostFactory来简化我们的配置。webServiceHost继承自ServiceHost,使用它,我们将不再需要对webHttpBinding与
webHttpBehavior进行配置,webServiceHost会自动创建端点,并使用webHttpBinding与webHttpBehavior对其进行配置。
  MSDN中对webServiceHost的说明如下:
  如果 WebServiceHost 在服务说明中找不到终结点,则它将在服务的基址中自动为 HTTP 和 HTTPS 基址创建一个默认终结点。如果用户已在基址中明确配置
终结点,则它不会自动创建终结点。WebServiceHost 会自动配置终结点的绑定,以便在安全虚拟目录中使用时与关联的 Internet 信息服务 (IIS) 安全设置一起使用。
当创建默认 HTTP 终结点时,WebServiceHost 同时禁用 HTTP 帮助页和 Web 服务描述语言 (WSDL) GET 功能,以使元数据终结点不干扰默认 HTTP 终结点。
  此外,WebServiceHost 类会将 WebHttpBehavior 添加到所有没有该行为但具有 WebMessageEncodingElement 的终结点中。如果服务上的所有操作都具有空的 HTTP 请求正文,或者都将 HTTP 请求正文以流的形式处理,则 WebServiceHost 会自动为绑定配置适当的内容类型映射器。
  请注意:在3.5中它会禁用REST WCF中的帮助(help)页面。
WebServiceHostFactory ,继承自ServiceHostFactory,使用它,我们将无需对REST WCF进行配置,只需在svc文件的指令中指定它即可。示例如下:
<%@ ServiceHost Service="LogServices.LogServices" Factory="System.ServiceModel.Activation.WebServiceHostFactory"%>
需要说明的是:Service属性需指明是实现服务契约的类。
第二部分:Demo示例程序。
开发环境:VS2008 SP1
程序结构如下图:


Contracts:定义了服务契约。
代码如下:

[ServiceContract]
public interface Ilog
{
[OperationContract]
[WebGet(UriTemplate = "/")]
List<LogEntity> GetAll();

[OperationContract]
[WebGet(UriTemplate = "Get/{year}/{month}")]
List<LogEntity> GetMonthLog(string year,stringmonth);
}
[DataContract]
public class LogEntity
{
[DataMember]
public int ID { get; set; }

[DataMember]
public string EventName { get; set; }

[DataMember]
public string Level { get; set; }

[DataMember]
public DateTime Time { get; set; }

}

  

LogServices:实现服务契约,提供服务
代码如下:

public List<LogEntity> GetAll()
{
return GetLogEntityList();
}

public List<LogEntity> GetMonthLog(string year, string month)
{
List<LogEntity> logEntities = GetLogEntityList();
List<LogEntity> logList = new List<LogEntity>();
logEntities.ForEach(log =>
{
if (log.Time.Year.ToString() == year && log.Time.Month.ToString() == month)
{
logList.Add(log);
}
});
return logList;

}

  

顺便大家可以看看配置文件,使用WebServiceHostFactory不需要对WCF进行配置。配置文件如下:

<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>
<appSettings/>
<connectionStrings/>
<system.web>
<!-- 
设置 compilation debug="true" 可将调试符号插入
已编译的页面中。但由于这会 
影响性能,因此只在开发过程中将此值 
设置为 true。
-->
<compilation debug="true">
<assemblies>
<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<!--
通过 <authentication> 节可以配置 ASP.NET 用来 
识别进入用户的
安全身份验证模式。 
-->
<authentication mode="Windows"/>
<!--
如果在执行请求的过程中出现未处理的错误,
则通过 <customErrors> 节可以配置相应的处理步骤。具体说来,
开发人员通过该节可以配置
要显示的 html 错误页
以代替错误堆栈跟踪。

<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</controls>
</pages>
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<providerOption name="CompilerVersion" value="v3.5"/>
<providerOption name="WarnAsError" value="false"/>
</compiler>
</compilers>
</system.codedom>
<!-- 
在 Internet 信息服务 7.0 下运行 ASP.NET AJAX 需要 system.webServer
节。对早期版本的 IIS 来说则不需要此节。
-->
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<remove name="ScriptModule"/>
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers>
<remove name="WebServiceHandlerFactory-Integrated"/>
<remove name="ScriptHandlerFactory"/>
<remove name="ScriptHandlerFactoryAppServices"/>
<remove name="ScriptResource"/>
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<!--<system.serviceModel>

</system.serviceModel>-->
</configuration>

  

RESTWCFDemo:寄宿服务,并调用服务。由于REST WCF服务的寄宿需要IIS,所以程序的客户端与服务端直接放在了一个Web工程下面。
直接在IE中调用服务查看运行效果。
1、调用List<LogEntity> GetAll()。结果如下图:

2、调用GetMonthLog。结果如下图:

posted @ 2011-10-26 11:21  tyb1222  阅读(3391)  评论(4编辑  收藏  举报