路由集成 - WCF 功能






3.78/5 (7投票s)
WCF 与路由集成是 .NET 4.0 中新引入的一项功能,非常实用。
引言
想象一下,您正在使用 WCF 编写 RESTful 服务,并决定将其托管在 IIS 中。
那么,就会出现一些问题,让我们来解答它们。
问:您将如何在 IIS 中托管 WCF 服务?
答:通过将 .svc 文件放置在虚拟目录中。
问:您将如何向该服务发出请求?
答:通过使用类似于以下内容的 URI:http://some.com/SomeService.svc。
问:上述 URI 是否是 REST 服务的最佳选择?
答:不是。
问:为什么?
答:RESTful 服务的 URI 用于标识特定的资源,而带有 .svc 等扩展名是不好的。
问:为什么?
答:即使 URI 中包含 .svc 在技术上并非不符合 REST 规范,但我们是否通过显示 .svc 泄露了实现细节?
背景
.Net 4.0 为 WCF 添加了许多功能,其中之一就是 System.Web.Routing 集成。此功能允许 WCF RESTful 服务在没有 .svc 的情况下在 IIS 中托管。
答:路由 只是一个 URL 模式,它映射到一个处理程序,在这种情况下,处理程序将是一个服务请求的类。
示例代码(服务器)
让我们构建一个执行算术级数和几何级数求和的 RESTful 服务。
步骤 1:创建一个新的 WCF 项目并删除 .svc 文件。
步骤 2:添加一个文件 IService.cs。
步骤 3:添加两个文件 GPService.cs 和 APService.cs。
步骤 4:如果不存在,则添加 Global.asax 文件。
路由集成步骤
1:在 Global.asax.cs 中的 Application_Start 事件中,将 ServiceRoute 添加到 RouteTable。添加的这些路由表示服务在被调用时将响应的虚拟 URI。
2:在 Web.config 文件中,添加 System.Web.Routing.UrlRoutingModule 模块,并将 runAllManagedModulesForAllRequests 属性设置为 true。 此外,将 UrlRoutingHandler 处理程序添加到 <system.webServer> 元素,如下所示。
3:启用 ASP.NET 兼容性要求,以用于实现服务的类(在本例中为 GPService 和 APService)。
// IService.cs file is as below. Nothing special here. Contains one operation contract and one data contract.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace RoutingIntegration
{
[ServiceContract]
public interface ISeriesService
{
/// <summary>
/// Calculates the sequence summation.
/// </summary>
/// <param name="input">Input is of type SeqInput</param>
/// <returns>SeqInput updated with the answer</returns>
[OperationContract]
SeqInput CalculateSeriesSummation(SeqInput input);
}
/// <summary>
/// Data contract used as a entity which contains variables required for sequence summation
/// Once calculated answer is assigned back and this class is sent back as response.
/// </summary>
[DataContract]
public class SeqInput
{
[DataMember]
public int numberOfItems;
[DataMember]
public int firstTerm;
[DataMember]
public int commonDiff;
[DataMember]
public double Answer;
public SeqInput(int numberOfItems1, int firstTerm1, int commonDiff1)
{
this.numberOfItems = numberOfItems1;
this.firstTerm = firstTerm1;
this.commonDiff = commonDiff1;
}
public override string ToString()
{
return String.Format("{0} {1} {2}", numberOfItems, firstTerm, commonDiff);
}
}
}
//GPService.cs as below. Simply impliments the ISeriesService.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.ServiceModel.Activation;
using System.ComponentModel;
namespace RoutingIntegration
{
//Applied to a service to indicate whether that service can be run in ASP.NET compatibility code.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class GPService : ISeriesService
{
//Indicates a service operation is logically an invoke
// operation and that it can be called by the REST programming model.
[WebInvoke(Method = "POST", UriTemplate = ""), Description("Geometric Sequences.")]
public SeqInput CalculateSeriesSummation(SeqInput input)
{
double answer = 0;
answer = (double.Parse((Math.Pow(input.commonDiff,
input.numberOfItems) - 1).ToString() )/ (input.commonDiff - 1));
answer = answer * input.firstTerm;
input.Answer = answer;
return input;
}
}
}
//APService.cs as below. Simply impliments the ISeriesService.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.ServiceModel.Activation;
using System.ComponentModel;
namespace RoutingIntegration
{
//Applied to a service to indicate whether that service can be run in ASP.NET compatibility code.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class APService : ISeriesService
{
//Indicates a service operation is logically an invoke
// operation and that it can be called by the REST programming model.
[WebInvoke(Method = "POST", UriTemplate = ""), Description("Arithmetic Sequences.")]
public SeqInput CalculateSeriesSummation(SeqInput input)
{
double answer = 0;
answer = ((2 * input.firstTerm) + (input.numberOfItems - 1) * input.commonDiff);
answer = answer * (double.Parse(input.numberOfItems.ToString()) / 2);
input.Answer = answer;
return input;
}
}
}
//Global.asax.cs, Most important part.
using System;
using System.Collections.Generic;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using System.Web.Routing;
namespace RoutingIntegration
{
public class Global : System.Web.HttpApplication
{
/// <summary>
/// Adds the ServiceRoute to the RouteTable
/// </summary>
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("GPService", new WebServiceHostFactory(), typeof(GPService)));
RouteTable.Routes.Add(new ServiceRoute("APService", new WebServiceHostFactory(), typeof(APService)));
}
protected void Session_Start(object sender, EventArgs e)
{
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
}
protected void Application_Error(object sender, EventArgs e)
{
}
protected void Session_End(object sender, EventArgs e)
{
}
protected void Application_End(object sender, EventArgs e)
{
}
}
}
<!--This is how Web.config looks-->
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<!-- Below part that needs to be added for routing integration -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule"
type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
<handlers>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd"/>
</handlers>
</system.webServer>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below
to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<!--Set the aspNetCompatibilityEnabled attribute to true"-->
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled ="true" />
</system.serviceModel>
</configuration>
示例代码(客户端)
让我们构建一个小型客户端来使用我们上面创建的 RESTful 服务。
为了演示,我只使用 APService。
读者可以发挥创造力,创建一个控制台应用程序、WinForms 应用程序或 Web 应用程序并设计一个消耗方式。我将只解释服务消耗的主要部分。
步骤 1:我们需要在客户端中也包含我们的 SeqInput
类,因为服务的 CalculateSeriesSummation
方法接受类型为 SeqInput
的参数,并在更新答案后返回该参数。
namespace RoutingIntegration
{
[DataContract]
public class SeqInput
{
[DataMember]
public int numberOfItems;
[DataMember]
public int firstTerm;
[DataMember]
public int commonDiff;
[DataMember]
public double Answer;
public SeqInput(int numberOfItems1, int firstTerm1, int commonDiff1)
{
this.numberOfItems = numberOfItems1;
this.firstTerm = firstTerm1;
this.commonDiff = commonDiff1;
}
public override string ToString()
{
return String.Format("{0} {1} {2}", numberOfItems, firstTerm, commonDiff);
}
}
}
第二步
static readonly DataContractSerializer customerSerializer =
new DataContractSerializer(typeof(RoutingIntegration.SeqInput));
try
{
Uri address = null;
HttpWebRequest request = null;
RoutingIntegration.SeqInput finalAnswer = null;
address = new Uri("https://:1254/APService");
request = (HttpWebRequest)WebRequest.Create(address);
request.Method = "POST";
request.ContentType = "application/xml";
RoutingIntegration.SeqInput si = new RoutingIntegration.SeqInput(1,1,1);
using (Stream requestStream = request.GetRequestStream())
{
customerSerializer.WriteObject(requestStream, si);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
finalAnswer = (RoutingIntegration.SeqInput)customerSerializer.ReadObject(responseStream);
}
response.Close();
lblAnswer.Text = "Answer is: " + finalAnswer.Answer.ToString();
}
catch (Exception ex)
{
//Handle the exception
}
兴趣点
.NET 4.0 为 WCF 提供的的新功能非常有趣,并且让每个人都感到好奇。