操作格式化程序和操作选择器

QueryStringFormatter 示例演示 Windows Communication Foundation (WCF) 扩展性点如何可用于允许 WCF 所需的其他格式的消息数据。 默认情况下,WCF 格式化程序要求在 soap:body 元素下包含方法参数。 但是,此示例演示如何实现一个自定义操作格式化程序,用于分析 HTTP GET 查询字符串中的参数数据并使用该数据调用方法。

此示例基于入门指南中的示例,它实现 ICalculator 服务协定。 它演示如何更改 Add、Subtract、Multiply 和 Divide 消息,以便对客户端到服务器请求使用 HTTP GET,对服务器到客户端响应使用带有 POX 消息的 HTTP POST。

为此,此示例提供了以下功能:

  • QueryStringFormatter,它分别为客户端和服务器实现 IClientMessageFormatterIDispatchMessageFormatter,并处理查询字符串中的数据。

  • UriOperationSelector,它在服务器上实现 IDispatchOperationSelector,以便基于 GET 请求中的操作名称执行操作调度。

  • EnableHttpGetRequestsBehavior 终结点行为(和对应的配置),它向运行库中添加必要的操作选择器。

  • 演示如何在运行库中插入新的操作格式化程序。

  • 在此示例中,客户端和服务都是控制台应用程序 (.exe)。

注意

本主题的最后介绍了此示例的设置过程和生成说明。

关键概念

QueryStringFormatter - 操作格式化程序是 WCF 的一个组件,它负责将消息转换为参数对象数组,并将参数对象数组转换为消息。 这在客户端是使用 IClientMessageFormatter 接口完成的,在服务器上是使用 IDispatchMessageFormatter 接口完成的。 通过这些接口,用户可以从 SerializeDeserialize 方法获得请求和响应消息。

在此示例中,QueryStringFormatter 同时实现了这两个接口,并且在客户端和服务器上均已实现。

请求:

  • 此示例使用 TypeConverter 类将请求消息中的参数数据转换为字符串,并将字符串转换为参数数据。 如果 TypeConverter 对某个特定的类型不可用,示例格式化程序将引发异常。

  • 在客户端上的 IClientMessageFormatter.SerializeRequest 方法中,格式化程序创建具有相应“收件人”地址的 URI,并将操作名称作为后缀附加在后面。 此名称用于调度给服务器上的相应操作。 然后它采用参数对象数组,并使用 TypeConverter 类转换的参数名称和值将参数数据序列化为 URI 查询字符串。 接着,ToVia 属性设置为该 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)都必须成功。

设置、生成和运行示例
  1. 请确保已执行 Windows Communication Foundation 示例的一次性安装过程

  2. 要生成解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。

  3. 要使用单机配置或跨计算机配置来运行示例,请按照运行 Windows Communication Foundation 示例中的说明进行操作。