65.9K
CodeProject 正在变化。 阅读更多。
Home

路由集成 - WCF 功能

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.78/5 (7投票s)

2012 年 4 月 16 日

CPOL

2分钟阅读

viewsIcon

29530

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 提供的的新功能非常有趣,并且让每个人都感到好奇。

© . All rights reserved.