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






4.94/5 (49投票s)
本文介绍了 WCF 路由服务概念、配置路由服务(其终结点、目标服务、消息过滤器和过滤器表)以及基于内容的路由。
目录
所有文章
- WCF 路由服务 - 第 IV 部分:服务版本控制和多播
- WCF 路由服务 - 第 III 部分:故障转移和负载均衡
- WCF 路由服务 - 第 II 部分:基于上下文的路由和协议桥接
- 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 | 定义处理单播会话通道消息所需的接口。 |
请注意,ISimplexDatagramRouter
和 IRequestReplyRouter
接口定义了通用的单向和请求-应答服务协定定义,可以与业务特定的服务协定结合使用。而另外两个接口 ISimplexSessionRouter
和 IDuplexSessionRouter
是需要会话的服务协定。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
支持的服务协定(如上所述:IRequestReplyRouter
、ISimplexDatagramRouter
、ISimplexSessionRouter
、IDuplexSessionRouter
)来配置一个或多个 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>
在上面,第一个终结点使用 basicHttpBinding
和 IRequestReplyRouter
服务协定(请求-应答),第二个终结点使用 wsHttpBinding
和 ISimplexDatagramRouter
服务协定(单向)。上面配置的终结点基本上是路由终结点(虚拟终结点或消息代理),客户端应用程序将使用它们。客户端应用程序可以使用这些终结点之一来调用实际服务,并且每个服务调用将直接定向到 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
服务的),而不是 IRequestReplyRouter
(RoutingService
的)。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
方法的代码)。
在运行我们的演示之前,请快速浏览一下本帖随附的示例项目-
解决方案中有四个项目:CalculatorService、ConsoleClient、ConsoleHostComplexNo 和 ConsoleHostRouter。现在,将 ConsoleClient、ConsoleHostComplexNo 和 ConsoleHostRouter 项目设置为启动项目,然后按 Ctrl+F5 键启动项目。现在在控制台客户端上按任意键,您就可以验证路由是否正常工作。您可以看到消息在被中介 RoutingService
“路由”后到达 ComplexNumberCalculator
服务。
基于内容的路由
在基于内容的路由技术中,目标服务是通过评估特定传入消息的内容来确定的。您可以评估传入消息的头或体来决定目标服务终结点。您可以检查传入消息的 SOAP 操作,或者消息负载中的某些值,例如元素、属性或头值等。您可以使用 Action
、XPath
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>
就是这样。现在将 ConsoleClient 和 ConsoleHostRouter 项目设置为启动项目,然后按 Ctrl+F5 键运行项目。接下来,从Visual Studio Developer Command Prompt(以管理员模式)运行 WCFRoutingPart1\ComplexNumberServices\StartAllServices.cmd 文件(请参见示例代码),以启动 ComplexNumberService1
和 ComplexNumberService2
服务。最后,在控制台客户端上按任意键,您就可以验证二元运算是否已路由到 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 日 -- 发布了原始版本