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

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

此示例基于名为Getting Started的指南,并实现ICalculator服务协定。 它展示了如何将加法、减法、乘法和除法消息更改为使用 HTTP GET 进行客户端到服务器的请求,以及使用 POX 消息的 HTTP POST 进行服务器到客户端的响应。

为此,此示例提供以下内容:

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

  • UriOperationSelector,它在服务器上实现了 IDispatchOperationSelector,根据 GET 请求中的操作名称执行操作调度。

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

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

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

注释

本示例的设置过程和生成说明位于本主题末尾。

关键概念

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

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

请求:

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

  • 在客户端的IClientMessageFormatter.SerializeRequest方法中,格式化程序创建一个带有适当 To 地址的 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() 之前添加操作格式化程序。

运行示例时,操作请求和响应将显示在客户端控制台窗口中。 所有四个运算(加、减、乘和除)都必须成功。

设置、生成和运行示例
  1. 确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。

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

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