操作格式化程序和操作选择器
QueryStringFormatter 示例演示 Windows Communication Foundation (WCF) 扩展性点如何可用于允许 WCF 所需的其他格式的消息数据。 默认情况下,WCF 格式化程序要求在 soap:body
元素下包含方法参数。 但是,此示例演示如何实现一个自定义操作格式化程序,用于分析 HTTP GET 查询字符串中的参数数据并使用该数据调用方法。
此示例基于入门指南中的示例,它实现 ICalculator
服务协定。 它演示如何更改 Add、Subtract、Multiply 和 Divide 消息,以便对客户端到服务器请求使用 HTTP GET,对服务器到客户端响应使用带有 POX 消息的 HTTP POST。
为此,此示例提供了以下功能:
QueryStringFormatter
,它分别为客户端和服务器实现 IClientMessageFormatter 和 IDispatchMessageFormatter,并处理查询字符串中的数据。UriOperationSelector
,它在服务器上实现 IDispatchOperationSelector,以便基于 GET 请求中的操作名称执行操作调度。EnableHttpGetRequestsBehavior
终结点行为(和对应的配置),它向运行库中添加必要的操作选择器。演示如何在运行库中插入新的操作格式化程序。
在此示例中,客户端和服务都是控制台应用程序 (.exe)。
注意
本主题的最后介绍了此示例的设置过程和生成说明。
关键概念
QueryStringFormatter
- 操作格式化程序是 WCF 的一个组件,它负责将消息转换为参数对象数组,并将参数对象数组转换为消息。 这在客户端是使用 IClientMessageFormatter 接口完成的,在服务器上是使用 IDispatchMessageFormatter 接口完成的。 通过这些接口,用户可以从 Serialize
和 Deserialize
方法获得请求和响应消息。
在此示例中,QueryStringFormatter
同时实现了这两个接口,并且在客户端和服务器上均已实现。
请求:
此示例使用 TypeConverter 类将请求消息中的参数数据转换为字符串,并将字符串转换为参数数据。 如果 TypeConverter 对某个特定的类型不可用,示例格式化程序将引发异常。
在客户端上的
IClientMessageFormatter.SerializeRequest
方法中,格式化程序创建具有相应“收件人”地址的 URI,并将操作名称作为后缀附加在后面。 此名称用于调度给服务器上的相应操作。 然后它采用参数对象数组,并使用 TypeConverter 类转换的参数名称和值将参数数据序列化为 URI 查询字符串。 接着,To 和 Via 属性设置为该 URI。 通过 MessageProperties 属性访问 Properties。在服务器上的
IDispatchMessageFormatter.DeserializeRequest
方法中,格式化程序在传入请求消息属性中检索Via
URI。 它将 URI 查询字符串中的名称-值对分析为参数名称和值,并使用参数名称和值填充传递给该方法的参数数组。 请注意,因为操作调度已经发生,所以此方法中忽略了操作名称后缀。
响应:
- 在此示例中,HTTP GET 只用于请求。 该格式化程序将响应的发送委托给本来用于生成 XML 消息的原始格式化程序。 此示例的目的之一是演示如何实现此类委托格式化程序。
UriPathSuffixOperationSelector 类
用户可以通过 IDispatchOperationSelector 接口实现自己的逻辑,决定应为哪个操作调度特定的消息。
在此示例中,必须在服务器上实现 UriPathSuffixOperationSelector
以选择合适的操作,因为操作名称包含在 HTTP GET URI 中,而不是包含在消息的操作标头中。 此示例设置为只允许不区分大小写的操作名称。
SelectOperation
方法采用传入消息,并在其消息属性中查找 Via
URI。 它从 URI 中提取操作名称后缀,查看内部表以获取应将该消息调度给的操作名称,并返回该操作名称。
EnableHttpGetRequestsBehavior 类
UriPathSuffixOperationSelector
组件可以通过编程方式或者通过终结点行为进行设置。 此示例实现了 EnableHttpGetRequestsBehavior
行为,这是在服务的应用程序配置文件中指定的。
在服务器上:
OperationSelector 设置为 IDispatchOperationSelector 实现。
默认情况下,WCF 使用精确匹配的地址筛选器。 传入消息中的 URI 包含一个操作名称后缀,后跟包含参数数据的查询字符串,因此终结点行为还将地址筛选器更改为前缀匹配筛选器。 它使用 WCFPrefixEndpointAddressMessageFilter 实现此目的。
安装操作格式化程序
指定格式化程序的操作行为是唯一的。 默认情况下始终为每个操作实现这样一个行为,以创建必要的操作格式化程序。 但是,这些行为看起来就像另一个操作行为;它们不能通过其他任何属性进行标识。 若要安装替换行为,该实现必须查找默认情况下由 WCF 类型加载程序安装的特定格式化程序行为,并替换该行为或者添加一个兼容的行为,以便在默认行为之后运行。
这些操作格式化程序行为可以在调用 CommunicationObject.Open 之前通过编程方式进行设置,或者通过指定一个在默认行为之后执行的操作行为来进行设置。 但是,通过终结点行为(并进而通过配置)并不能轻松完成设置,因为行为模式不允许用一个行为替换另一个行为,或者通过其他方式修改说明树。
在客户端上:
必须实现 IClientMessageFormatter 实现,以使其能够将请求转换为 HTTP GET 请求,并委托原始格式化程序做出响应。 这是通过调用 EnableHttpGetRequestsBehavior.ReplaceFormatterBehavior
帮助器方法实现的。
这必须在调用 CreateChannel
之前完成。
void ReplaceFormatterBehavior(OperationDescription operationDescription, EndpointAddress address)
{
// Remove the DataContract behavior if it is present.
IOperationBehavior formatterBehavior = operationDescription.Behaviors.Remove<DataContractSerializerOperationBehavior>();
if (formatterBehavior == null)
{
// Remove the XmlSerializer behavior if it is present.
formatterBehavior = operationDescription.Behaviors.Remove<XmlSerializerOperationBehavior>();
...
}
// Remember what the innerFormatterBehavior was.
DelegatingFormatterBehavior delegatingFormatterBehavior = new DelegatingFormatterBehavior(address);
delegatingFormatterBehavior.InnerFormatterBehavior = formatterBehavior;
operationDescription.Behaviors.Add(delegatingFormatterBehavior);
}
在服务器上:
必须实现 IDispatchMessageFormatter 接口,以使其能够读取 HTTP GET 请求,并委托原始格式化程序编写响应。 这是通过调用与客户端相同的
EnableHttpGetRequestsBehavior.ReplaceFormatterBehavior
帮助器方法实现的(请参见前面的代码示例)。这必须在调用 Open 之前完成。 在此示例中,我们演示如何在调用 Open 之前手动修改格式化程序。 实现同一目标的另一种方式是从 ServiceHost 派生一个类,以便在打开之前调用
EnableHttpGetRequestsBehavior.ReplaceFormatterBehavior
(有关示例,请参见承载文档和示例)。
用户体验
在服务器上:
服务器
ICalculator
实现不需要更改。服务的 App.config 必须使用自定义 POX 绑定,该绑定将
messageVersion
元素的textMessageEncoding
属性设置为None
。<bindings> <customBinding> <binding name="poxBinding"> <textMessageEncoding messageVersion="None" /> <httpTransport /> </binding> </customBinding> </bindings>
服务的 App.config 还必须指定自定义
EnableHttpGetRequestsBehavior
(通过将其添加到行为扩展部分并使用它来实现)。<behaviors> <endpointBehaviors> <behavior name="enableHttpGetRequestsBehavior"> <enableHttpGetRequests /> </behavior> </endpointBehaviors> </behaviors> <extensions> <behaviorExtensions> <!-- Enabling HTTP GET requests: Behavior Extension --> <add name="enableHttpGetRequests" type="Microsoft.ServiceModel.Samples.EnableHttpGetRequestsBehaviorElement, QueryStringFormatter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </behaviorExtensions> </extensions>
在调用 Open 之前添加操作格式化程序。
在客户端上:
客户端实现不需要更改。
客户端 App.config 必须使用自定义 POX 绑定,该绑定将
messageVersion
元素的textMessageEncoding
属性设置为None
。 与服务的一个区别在于,客户端必须允许手动寻址,以便修改传出“收件人”地址。<bindings> <customBinding> <binding name="poxBinding"> <textMessageEncoding messageVersion="None" /> <httpTransport manualAddressing="True" /> </binding> </customBinding> </bindings>
客户端 App.config 必须指定与服务器相同的自定义
EnableHttpGetRequestsBehavior
。在调用 CreateChannel() 之前添加操作格式化程序。
运行示例时,操作请求和响应将显示在客户端控制台窗口中。 所有四个操作(Add、Subtract、Multiply 和 Divide)都必须成功。
设置、生成和运行示例
要生成解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。
要使用单机配置或跨计算机配置来运行示例,请按照运行 Windows Communication Foundation 示例中的说明进行操作。