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

WCF 路由服务 - 第一部分:基本概念、简单路由服务与基于内容的路由

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (49投票s)

2014年5月13日

CPOL

14分钟阅读

viewsIcon

112290

downloadIcon

2285

本文介绍了 WCF 路由服务概念、配置路由服务(其终结点、目标服务、消息过滤器和过滤器表)以及基于内容的路由。

目录

所有文章

引言

路由服务是一个通用的 SOAP 中介,充当路由器。路由服务的核心功能是能够根据消息内容(消息头或消息体)将传入的消息路由到与路由服务相同计算机上托管的实际服务,或者路由到分布在网络上的实际服务。实际上,路由服务充当一个前端服务,镜像目标服务。路由服务的主要优点是为客户端(应用程序)提供位置透明性,因为客户端明确地与任何实际执行其任务的实际服务解耦。因此,它可以在路由服务中执行各种不同的中间处理。

您可以通过多种方式使用路由。例如,您可以使用基于内容/基于上下文的路由技术将传入消息路由到适当的服务。您还可以使用路由来实现集中的安全边界、协议桥接、负载平衡甚至服务版本管理。

在路由中,路由服务(路由器)公开一个虚拟终结点,客户端应用程序消费该虚拟终结点,而不是直接消费实际服务终结点,该虚拟终结点通过中介将来自客户端应用程序的传入消息路由到适当的实际服务终结点。

WCF 4.0 之前,该框架没有官方支持路由服务,但从 WCF 4.0 开始,它提供了内置支持。

理解路由服务

WCF 4.0 引入了一个名为 RoutingService 的新类,它提供了一个通用的 WCF 路由实现。RoutingService 类可以处理通过各种不同的消息传递模式(如单向、请求-应答和双工消息传递)路由消息。该类位于 System.ServiceModel.Routing 命名空间下,您需要在服务托管应用程序中添加 System.ServiceModel.Routing.dll 程序集的引用。

以下是 RoutingService 类的定义(来自 msdn

[AspNetCompatibilityRequirementsAttribute(RequirementsMode =
                                               AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehaviorAttribute(AddressFilterMode = AddressFilterMode.Any, InstanceContextMode
                        = InstanceContextMode.PerSession, UseSynchronizationContext = false,
                                                            ValidateMustUnderstand = false)]
public sealed class RoutingService : ISimplexDatagramRouter, ISimplexSessionRouter,
                                      IRequestReplyRouter, IDuplexSessionRouter, IDisposable
{ ... }

从上面可以看出,RoutingService 类被定义为密封类,并实现了多个服务协定以支持多种消息交换模式(MEP)。每个服务协定都支持一种不同的消息交换模式(MEP)。请参见下表了解详情(来自 msdn)-

服务合同 描述
IDuplexSessionRouter 定义处理双工会话通道消息所需的接口。
IRequestReplyRouter 定义处理请求-应答通道消息所需的接口。
ISimplexDatagramRouter 定义处理单播数据报通道消息所需的接口。
ISimplexSessionRouter 定义处理单播会话通道消息所需的接口。

请注意,ISimplexDatagramRouterIRequestReplyRouter 接口定义了通用的单向和请求-应答服务协定定义,可以与业务特定的服务协定结合使用。而另外两个接口 ISimplexSessionRouterIDuplexSessionRouter 是需要会话的服务协定。ISimplexSessionRouter 基本上是在会话范围内发生的“一次性发送”操作,而 IDuplexSessionRouter 基本上是双工会话感知操作,需要在会话范围内回调客户端应用程序。请参见下面这些接口的定义-

[ServiceContract(Namespace = "http://schemas.microsoft.com/netfx/2009/05/routing",
            SessionMode = SessionMode.Allowed)]
public interface ISimplexDatagramRouter
{
   [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
   IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);

   void EndProcessMessage(IAsyncResult result);
}

[ServiceContract(Namespace = "http://schemas.microsoft.com/netfx/2009/05/routing",
            SessionMode = SessionMode.Allowed)]
public interface IRequestReplyRouter
{
   [OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
   [GenericTransactionFlow(TransactionFlowOption.Allowed)]
   IAsyncResult BeginProcessRequest(Message message, AsyncCallback callback, object state);

   Message EndProcessRequest(IAsyncResult result);
}

[ServiceContractAttribute(Namespace = "http://schemas.microsoft.com/netfx/2009/05/routing",
            SessionMode = SessionMode.Required)]
public interface ISimplexSessionRouter
{
   [OperationContractAttribute(AsyncPattern = true, IsOneWay = true, Action = "*")]
   IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, Object state);

   void EndProcessMessage(IAsyncResult result);
}

public interface IDuplexSessionRouter
{
   [OperationContractAttribute(AsyncPattern = true, IsOneWay = true, Action = "*")]
   IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, Object state);

   void EndProcessMessage(IAsyncResult result;)
}

RoutingService 类的主要目的是通过虚拟终结点接收来自客户端应用程序的传入消息,并通过将每个传入消息与一组消息过滤器进行评估来将它们“路由”到适当的实际服务。因此,您可以通过定义消息过滤器来控制路由行为,通常在配置文件中进行。

托管路由服务

您可以像托管其他 WCF 服务一样,使用自托管或托管技术来托管 RoutingService。下面是使用 ServiceHost 类托管 RoutingService 的自托管技术的典型示例-

var host = new ServiceHost(typeof(RoutingService));

try
{
   host.Open();
   Console.ReadLine();
   host.Close();
}
catch (Exception ex)
{
   Console.WriteLine(ex.Message);
   host.Abort();
}

与其它 WCF 服务一样,您也可以通过配置文件来配置 RoutingService,在其中定义 RoutingService 终结点、RoutingService 行为、路由过滤器以及最终将传入消息路由到的实际服务终结点。让我们在接下来的章节中尝试理解这些概念。

配置路由服务终结点

您可以通过选择一个 WCF 绑定和 RoutingService 类实现的 RoutingService 支持的服务协定(如上所述:IRequestReplyRouterISimplexDatagramRouterISimplexSessionRouterIDuplexSessionRouter)来配置一个或多个 RoutingService 终结点。

以下是一个具有两个路由终结点的 RoutingService 示例。

<services>
   <service name="System.ServiceModel.Routing.RoutingService"><!--Routing Service -->
      <endpoint address="" binding="basicHttpBinding"
                        contract="System.ServiceModel.Routing.IRequestReplyRouter" name="MessageBroker" /> <!--MessageBroker-->
      <endpoint address="regular" binding="basicHttpBinding"
                        contract="System.ServiceModel.Routing.IRequestReplyRouter" name="Regular" /> <!--Regular-->
      <host>
         <baseAddresses>
            <add baseAddress="https://:8080/RoutingService/Router" />
         </baseAddresses>
      </host>
   </service>
</services>

在上面,第一个终结点使用 basicHttpBindingIRequestReplyRouter 服务协定(请求-应答),第二个终结点使用 wsHttpBindingISimplexDatagramRouter 服务协定(单向)。上面配置的终结点基本上是路由终结点(虚拟终结点或消息代理),客户端应用程序将使用它们。客户端应用程序可以使用这些终结点之一来调用实际服务,并且每个服务调用将直接定向到 RoutingService。当 RoutingService 通过这些路由终结点之一接收到消息时,它会根据一组消息过滤器评估消息,以确定将消息转发到何处。

以下是基于上述 RoutingService 配置的客户端应用程序终结点示例-

<client><!--Client Side Endpoints for Routing Service -->
   <endpoint address="https://:8080/RoutingService/Router" binding="basicHttpBinding"
                  contract="IComplexNumber" name="BasicHttpBinding_IComplexNumber" />
   <endpoint address="https://:8080/RoutingService/Router/regular" binding="basicHttpBinding"
                  contract="IRealNumber" name="BasicHttpBinding_IRealNumber" />
</client>

配置路由服务消息过滤器

您可以为 RoutingService 配置消息过滤器来管理路由消息过滤器。WCF 4.0 提供了 RoutingBehavior 来实现此功能。因此,为了用消息过滤器配置 RoutingService,您需要定义一个命名的行为配置(例如“routingFilters”),通过启用 RoutingBehavior,然后指定过滤器表的名称。之后,您需要通过 behaviorConfiguration 属性将“routingFilters”行为应用于 RoutingService。请看下面的示例-

<behaviors>
   <serviceBehaviors>
      <behavior name="routingFilters">
         <routing filterTableName="RoutingTable" />
      </behavior>
   </serviceBehaviors>
</behaviors>
<services>
   <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="routingFilters">
        ...
   </service>
</services>

配置路由服务目标服务

您需要定义您打算路由到的目标实际服务的终结点。您可以在 WCF <client> 配置节中定义这些目标终结点,如下所示-

<client>
   <endpoint address="https://:8081/ComplexNumberService" binding="basicHttpBinding"
                  contract="*" name="ComplexNumber" />
   <endpoint address="https://:8082/RealNumberService" binding="basicHttpBinding"
                  contract="*" name="RealNumber" />
</client>

在 contract 属性中的 “*” 字符允许服务接受任何传入消息,而不仅仅是特定服务协定指定的那些消息。

定义路由服务过滤器表

过滤器表在运行时决定路由逻辑。您可以在元素内定义过滤器表条目。其中的每个条目定义了路由“过滤器”和目标终结点之间的映射。您可以在元素内定义“过滤器”。每个条目指定过滤器的类型以及特定于过滤器的相关数据(例如:操作值、XPath 表达式、路由终结点名称等)。

以下是一个配置过滤器表“RoutingData”的示例,其中包含两个过滤器,映射到两个终结点。这里使用了 EndpointName 过滤器类型。

<routing>
   <filters>
      <filter name="ComplexNumberFilter" filterType="EndpointName" filterData="MessageBroker" />
      <filter name="RealNumberFilter" filterType="EndpointName" filterData="Regular" />
   </filters>
   <filterTables>
      <filterTable name="RoutingTable">
         <add filterName="ComplexNumberFilter" endpointName="ComplexNumber" />
         <add filterName="RealNumberFilter" endpointName="RealNumber" />
      </filterTable>
   </filterTables>
</routing>

WCF 4.0 提供了几种内置的消息 filterTypes,您可以使用它们来检查传入消息的内容。请参阅下表了解详细信息(来自 msdn)-

过滤器类型

描述

说明

操作

使用 ActionMessageFilter 类匹配包含特定操作的消息。

用于过滤的操作。

EndpointAddress

使用 EndpointAddressMessageFilter 类,并设置 IncludeHostNameInComparison == true 来匹配包含特定地址的消息。

用于过滤的地址(在 To 头中)。

EndpointAddressPrefix

使用 PrefixEndpointAddressMessageFilter 类,并设置 IncludeHostNameInComparison == true 来匹配包含特定地址前缀的消息。

使用最长前缀匹配进行过滤的地址。

并且

使用 StrictAndMessageFilter 类,该类在返回结果之前始终评估两个条件。

filterData 未使用;相反,filter1 和 filter2 是相应消息过滤器的名称(也在表中),这些过滤器需要被 AND 组合。

自定义

一个用户定义的类型,它扩展了 MessageFilter 类,并且有一个接受字符串的构造函数。

customType 属性是类的完全限定类型名称;filterData 是创建过滤器时传递给构造函数的字符串。

EndpointName

使用 EndpointNameMessageFilter 类,根据消息到达的服务终结点的名称进行匹配。

服务终结点的名称,例如:“serviceEndpoint1”。这应该是路由服务公开的终结点之一。

MatchAll

使用 MatchAllMessageFilter 类。此过滤器匹配所有传入的消息。

filterData 未使用。此过滤器将始终匹配所有消息。

XPath

使用 XPathMessageFilter 类来匹配消息中的特定 XPath 查询。

匹配消息时使用的 XPath 查询。

功能

  • 基于内容的路由
    • 服务聚合
    • 服务版本管理
    • 优先级路由
    • 动态配置
  • 基于上下文的路由
  • SOAP 处理
  • 协议桥接
  • 备用终结点
  • 负载均衡
  • 多播 (Multicasting)
  • 高级错误处理

演示服务

现在,在描述完 RoutingService 之后,让我们通过示例来理解它。我为此创建了一个演示服务 ComplexNumberCalculator。我定义了一个数据协定 Complex 和一个服务协定 IComplexNumber,然后通过实现 IComplexNumber 服务协定创建了一个 ComplexNumberCalculator 服务。请看下面的代码-

    [DataContract]
    public class Complex
    {
        [DataMember]
       public  double Real;

        [DataMember]
        public double Imaginary;
    }

    [ServiceContract]
    public interface IComplexNumber
    {
        [OperationContract]
        Complex Add(Complex x, Complex y);

        [OperationContract]
        Complex Subtract(Complex x, Complex y);

        [OperationContract]
        Complex Multiply(Complex x, Complex y);

        [OperationContract]
        Complex Divide(Complex x, Complex y);

        [OperationContract]
        double Modulus(Complex x);

        [OperationContract]
        double Argument(Complex x);

        [OperationContract]
        Complex Conjugate(Complex x);

        [OperationContract]
        Complex Recipocal(Complex x);
    }

    public class ComplexNumberCalculator : IComplexNumber
    {
        public Complex Add(Complex x, Complex y)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Add");

            var z = new Complex();

            z.Real = x.Real + y.Real;
            z.Imaginary = x.Imaginary + y.Imaginary;

            return z;
        }

        public Complex Subtract(Complex x, Complex y)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Subtract");

            var z = new Complex();

            z.Real = x.Real - y.Real;
            z.Imaginary = x.Imaginary - y.Imaginary;

            return z;
        }

        public Complex Multiply(Complex x, Complex y)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Multiply");

            var z = new Complex();

            z.Real = x.Real * y.Real - x.Imaginary * y.Imaginary ;
            z.Imaginary = x.Real * y.Imaginary + x.Imaginary * y.Real;

            return z;
        }

        public Complex Divide(Complex x, Complex y)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Divide");

            var z = new Complex();

            var modulusY = this.Modulus(y);

            z.Real = (x.Real * y.Real + x.Imaginary * y.Imaginary) / (modulusY * modulusY);
            z.Imaginary = (x.Imaginary * y.Real - x.Real * y.Imaginary) / (modulusY * modulusY);

            return z;
        }

        public double Modulus(Complex x)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Modulus");

            var modX = Math.Sqrt(x.Real * x.Real + x.Imaginary * x.Imaginary);

            return modX;
        }

        public Complex Conjugate(Complex x)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Conjugate");

            var z = new Complex();

            z.Real = x.Real;
            z.Imaginary = -1 * x.Imaginary;

            return z;
        }

        public double Argument(Complex x)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Argument");

            var argumentX = Math.Atan(x.Imaginary/x.Real);

            return argumentX;
        }

        public Complex Recipocal(Complex x)
        {
            Console.WriteLine("Invoked ComplexNumberCalculator Operation: Recipocal");

            var z = new Complex();

            var modulusX = this.Modulus(x);
            var conjugateX = this.Conjugate(x);

            z.Real = conjugateX.Real / (modulusX * modulusX);
            z.Imaginary = conjugateX.Imaginary / (modulusX * modulusX);

            return z;
        }

我使用自托管技术在 Windows 控制台应用程序中托管了 ComplexNumberCalculator 服务,如下所示-

var host = new ServiceHost(typeof(ComplexNumberCalculator));

try
{
   host.Open();
   Console.ReadLine();
   host.Close();
}
catch (Exception ex)
{
   Console.WriteLine(ex.Message);
   host.Abort();
} 

并配置了两个终结点,一个服务终结点和一个标准的 mex 终结点,以便交换元数据,如下所示。

<services>
   <service name="CalculatorService.ComplexNumberCalculator">
      <endpoint address="" binding="basicHttpBinding" contract="CalculatorService.IComplexNumber" />
      <endpoint address="mex" kind="mexEndpoint" />
      <host>
         <baseAddresses>
            <add baseAddress="https://:8081/ComplexNumberService" />
         </baseAddresses>
      </host>
  </service>
</services> 

我还通过定义一个默认行为(通过省略名称)启用了服务元数据,如下所示-

<behaviors>
   <serviceBehaviors>
      <behavior name="">
         <serviceMetadata />
      </behavior>
   </serviceBehaviors>
</behaviors> 

我将使用此服务进行本帖中的所有演示。

使用 MatchAll filterType 的简单路由服务

在此示例中,我将配置一个简单的 RoutingService,它将仅传递(路由)我们 ComplexNumberCalculator 服务客户端应用程序的所有传入消息到 ComplexNumberCalculator 服务。在这里 RoutingService 将仅充当中介。我在 Windows 控制台应用程序中使用自托管技术托管了 RoutingService。您可以根据需要将 RoutingService 托管在 IIS/WAS/Windows 服务/AppFabric 中。

首先,我已将 RoutingService 配置为以下终结点(虚拟终结点),如下所示-

<services>
   <service name="System.ServiceModel.Routing.RoutingService">
      <endpoint address="" binding="basicHttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter"
                          name="VirtualEndpoint"  />
      <host>
         <baseAddresses>
            <add baseAddress="https://:8080/RoutingService/Router" />
         </baseAddresses>
      </host>
   </service>
</services> 

请注意,我在这里使用了 IRequestReplyRouter 服务协定,因为我们的 ComplexNumberCalculator 服务支持请求-应答 MEP

然后我为我们的目标服务 ComplexNumberCalculator 定义了一个终结点,如下所示-

<client>
   <endpoint address="https://:8081/ComplexNumberService" binding="basicHttpBinding"
                  contract="*" name="ComplexNumber" />
</client>

接下来,我启用了 RoutingBehavior,然后指定了过滤器表的名称。我通过定义默认行为(如下所示)来实现这一点-

<behaviors>
   <serviceBehaviors>
      <behavior name="">
         <routing filterTableName="RoutingTable" />
      </behavior>
   </serviceBehaviors>
</behaviors> 

下一步是定义我们的过滤器表:RoutingTable,向其中添加条目。但是,由于过滤器表中的每个条目都定义了路由过滤器和目标终结点之间的映射,我们将先定义过滤器,然后再定义我们的过滤器表。我为我们的过滤器表定义了以下过滤器-

<routing>
   <filters>
      <filter name="ComplexNumberFilter" filterType="MatchAll" />
   </filters> 
... 

我使用了上述 MatchAll 过滤器类型,它匹配所有传入的消息。请注意,我们的目标是将所有传入消息从客户端传递到 ComplexNumberCalculator

最后,我配置了我们的过滤器表:RoutingTable,其中包含上面定义的过滤器类型,如下所示-

<filterTables>
   <filterTable name="RoutingTable">
      <add filterName="ComplexNumberFilter" endpointName="ComplexNumber" />
   </filterTable>
</filterTables> 

在上面,我添加了一个表条目,并将 filterName 属性设置为“ComplexNumberFilter”(上面定义的过滤器类型的名称),并将 endpointName 属性设置为“ComplexNumber”(目标服务终结点的名称,即最终接收者)。

最后,我创建了一个控制台客户端应用程序来调用该服务。我使用命令行中的 svcutil.exe 生成了 ComplexNumberCalculator 服务代码文件,如下所示-

svcutil.exe https://:8081/ComplexNumberService/mex 

并配置了客户端端终结点,如下所示-

<system.serviceModel>
<client>
   <endpoint address="https://:8080/RoutingService/Router" binding="basicHttpBinding"
                  contract="IComplexNumber" name="BasicHttpBinding_IComplexNumber" />
</client>
</system.serviceModel> 

请注意,我在这里使用了 IComplexNumber 服务协定(ComplexNumberCalculator 服务的),而不是 IRequestReplyRouterRoutingService 的)。IComplexNumber 服务协定将用于通过创建客户端通道来调用 ComplexNumberCalculator 服务的操作。下面是客户端应用程序代码-

var cf = new ChannelFactory<IComplexNumber>("BasicHttpBinding_IComplexNumber");

var channel = cf.CreateChannel();

var z1 = new Complex();
var z2 = new Complex();

z1.Real = 3D;
z1.Imaginary = 4D;

z2.Real = 2D;
z2.Imaginary = -2D;

Console.WriteLine("*** RoutingService with Message Filters ***\n");
Console.WriteLine("Please hit any key to start: ");
string command = Console.ReadLine();

while (command != "exit")
{
   ComplexNumberArithmetics(channel, z1, z2);

   Console.WriteLine("\nPlease hit any key to re-run OR enter 'exit' to exit.");
   command = Console.ReadLine();
}

((IClientChannel)channel).Close();

ComplexNumberArithmetics 方法使用上面创建的通道执行复数算术运算(您可以在示例中找到 ComplexNumberArithmetics 方法的代码)。

在运行我们的演示之前,请快速浏览一下本帖随附的示例项目-

解决方案中有四个项目:CalculatorServiceConsoleClientConsoleHostComplexNoConsoleHostRouter。现在,将 ConsoleClientConsoleHostComplexNoConsoleHostRouter 项目设置为启动项目,然后按 Ctrl+F5 键启动项目。现在在控制台客户端上按任意键,您就可以验证路由是否正常工作。您可以看到消息在被中介 RoutingService路由”后到达 ComplexNumberCalculator 服务。

基于内容的路由

在基于内容的路由技术中,目标服务是通过评估特定传入消息的内容来确定的。您可以评估传入消息的来决定目标服务终结点。您可以检查传入消息的 SOAP 操作,或者消息负载中的某些值,例如元素、属性或头值等。您可以使用 ActionXPath filterTypes 来实现基于内容的路由。

让我们举一个例子来理解我们 ComplexNumberCalculator 服务的基于内容的路由。假设我们要将二元运算(加、减、乘、除)路由到 ComplexNumberService1,将一元运算(模、幅角、共轭、倒数)路由到 ComplexNumberService2

我将通过两种方式实现此目标:第一种是使用 SOAP 头中的不同 ComplexNumberCalculator 操作值,第二种是使用 XPath 表达式

使用操作值进行基于内容的路由

让我们通过更新我们的 RoutingService 来开始基于操作值进行基于内容的路由。首先,我为目标服务定义了两个终结点,如下所示-

<client>
        <endpoint address="https://:8081/ComplexNumberService1" binding="basicHttpBinding"
                  contract="*" name="BinaryOperation" />
        <endpoint address="https://:8081/ComplexNumberService2" binding="basicHttpBinding"
                  contract="*" name="UnaryOperation" />
</client> 

接下来,我为每个不同的 ComplexNumberCalculator 服务操作值定义了过滤器,如下所示-

<filters>
          <!--Binary Operation-->
          <filter name="AddFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Add" />
          <filter name="SubtractFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Subtract" />
          <filter name="MultiplyFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Multiply" />
          <filter name="DivideFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Divide" />
          <!--Unary Operation-->
          <filter name="ModulusFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Modulus" />
          <filter name="ArgumentFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Argument" />
          <filter name="ConjugateFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Conjugate" />
          <filter name="RecipocalFilter" filterType="Action" filterData="http://tempuri.org/IComplexNumber/Recipocal" />
</filters> 

最后,我们在过滤器表:RoutigTable 中将二元运算映射到 ComplexNumberService1 终结点,将一元运算映射到 ComplexNumberService2 终结点,如下所示-

<filterTables>
          <filterTable name="RoutingTable">
            <add filterName="AddFilter" endpointName="BinaryOperation" />
            <add filterName="SubtractFilter" endpointName="BinaryOperation" />
            <add filterName="MultiplyFilter" endpointName="BinaryOperation" />
            <add filterName="DivideFilter" endpointName="BinaryOperation" />

            <add filterName="ModulusFilter" endpointName="UnaryOperation" />
            <add filterName="ArgumentFilter" endpointName="UnaryOperation" />
            <add filterName="ConjugateFilter" endpointName="UnaryOperation" />
            <add filterName="RecipocalFilter" endpointName="UnaryOperation" />
          </filterTable>
</filterTables> 

就是这样。现在将 ConsoleClientConsoleHostRouter 项目设置为启动项目,然后按 Ctrl+F5 键运行项目。接下来,从Visual Studio Developer Command Prompt(以管理员模式)运行 WCFRoutingPart1\ComplexNumberServices\StartAllServices.cmd 文件(请参见示例代码),以启动 ComplexNumberService1ComplexNumberService2 服务。最后,在控制台客户端上按任意键,您就可以验证二元运算是否已路由到 ComplexNumberservice1 服务,而一元运算是否已通过中介 RoutingService 路由到 ComplexNumberService2 服务。


使用 XPath 表达式进行基于内容的路由

您可以使用 XPath filterType 来评估传入消息的各种 XPath 表达式。它功能更强大、更灵活,您可以使用 XPath 表达式来检查和评估传入消息的任何部分,包括 SOAP 头或 SOAP 体。

让我们通过更新我们的 RoutingService 来开始使用 XPath filterType 进行基于内容的路由。首先,我使用 <namespaceTable> 元素定义了一组命名空间前缀绑定,如下所示-

<namespaceTable>
          <add prefix="s" namespace="http://schemas.xmlsoap.org/soap/envelope/" />
          <add prefix="wsa" namespace="http://schemas.microsoft.com/ws/2005/05/addressing/none" />
</namespaceTable> 

请参见下面的屏幕截图,了解我是如何定义命名空间前缀的-

接下来,我使用 XPath filterType 为每个不同的 ComplexNumberCalculator 服务操作值定义了过滤器,如下所示-

<filters>
          <!--Binary Operation-->
          <filter name="AddFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Add'" />
          <filter name="SubtractFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Subtract'" />
          <filter name="MultiplyFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Multiply'" />
          <filter name="DivideFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Divide'" />
          <!--Unary Operation-->
          <filter name="ModulusFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Modulus'" />
          <filter name="ArgumentFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Argument'" />
          <filter name="ConjugateFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Conjugate'" />
          <filter name="RecipocalFilter" filterType="XPath" filterData="/s:Envelope/s:Header/wsa:Action ='http://tempuri.org/IComplexNumber/Recipocal'" />
</filters> 

请注意,filterData 属性中包含的 XPath 表达式将针对传入消息进行评估(表达式仅检查操作值)。

现在,只需遵循上一个示例中的说明运行演示,您将看到与之前相同的结果。

XPath 过滤器技术非常有用,您可以使用它根据自定义 SOAP 头或 SOAP 消息体内的内容来路由消息。

结论

WCF 中的路由是一个非常广泛的话题。在本帖中,我介绍了WCF 路由服务概念,并解释了如何配置 RoutingService(终结点、目标服务、消息过滤器和过滤器表)。然后,我演示了使用 MatchAll filterType 的简单 RoutingService,最后探讨了使用操作值和 XPath 表达式的基于内容的路由。但仍有许多内容待讨论。在本系列的下一篇文章中,我将介绍一些路由主题,如协议桥接、基于上下文的路由、负载均衡等。在此之前,祝您编码愉快。

历史记录

  • 2014 年 6 月 7 日 -- 文章更新(在“所有文章”部分为系列的第四部分添加了新条目)
  • 2014 年 6 月 6 日 -- 文章更新(添加了“功能”部分)
  • 2014 年 5 月 28 日 -- 文章更新(在“所有文章”部分为系列的第三部分添加了新条目)
  • 2014 年 5 月 27 日 -- 文章更新(更新了“所有文章”部分中第二部分的 URL)
  • 2014 年 5 月 24 日 -- 文章更新
    • (更新了文章标题)
    • (更新了“所有文章”部分的条目)
  • 2014 年 5 月 22 日 -- 文章更新(纠正了拼写错误)
  • 2014 年 5 月 21 日 -- 文章更新(更新了“所有文章”部分的条目)
  • 2014 年 5 月 20 日 -- 文章更新(重新排列了“所有文章”部分的条目)
  • 2014 年 5 月 19 日 -- 文章更新
    • (更新了目录部分--添加了“所有文章”部分的条目)
    • (添加了“所有文章”部分)
  • 2014 年 5 月 14 日 -- 文章更新(添加了目录部分)
  • 2014 年 5 月 13 日 -- 发布了原始版本
© . All rights reserved.