WCF 扩展之我见: DispatchMessageFormatter

本系列索引,请见《开篇

说完了 CallContextInitializer,本文继续延着 WCF 运行时的处理流程介绍 DispatchMessageFormatter。

作 用

Formatter,顾名思义是为了进行格式化。而在 WCF 环境中,主要是指将 Message 中携带的 operation 调用参数反序列化成 .Net 中的方法参数,以及将方法的返回值序列化成 Message 中可携带的值。

我们可以利用这个扩展点来干涉序列化/反序列化的方式,比如客户端发送的 Message 是基于 JSON 的,比如需要对 Message 的正文进行 base64 编码,比如需要对 Message 进行压缩等。

执行时机

在开始调用 .NET 方法之前,线程上下文初始化之后,会调用 DeserializeInputs 对传入的 Message 进行反序列化。


在 InvokeBegin 后半截中,当 .Net 方法返回后,会先调用 ParameterInspector.AfterCall,随后就会执行 SerializeOutputs 对返回值进行序列化。

WCF 内置的实现方式

由于 Formatter 是 Message 与 .Net 方法互相转换必须的中间人,WCF 已经为我们提供了2个实现(都是 internal 的修饰符,只会由 WCF 自身使用):DataContractSerializerOperationFormatter 及 XmlSerializerOperationFormatter。前者基于 DataContractSerializer 是WCF默认的序列化器,后者基于 XmlSerializer。

这两个内置的 Formatter 都继承自 OperationFormatter,而这个 OperationFormatter 则实现了 IDispatchMessageFormatter。

如何扩展

先来看一下 IDispatchMesssageFormatter 的定义: 

    public interface IDispatchMessageFormatter
    {        
        void DeserializeRequest(Message message, object[] parameters);        
        Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result);
    }

DeserializeRequest 中的 parameters 是一个空的数组,数组的长度即是当前方法调用所需要的参数个数。当我们从 message 解析出参数后,按照实际方法定义中参数的顺序对 parameters 进行填充。


与其它扩展一样,需要自己定义一个类来实现该接口:

public class MyMessageFormatter : IDispatchMessageFormatter
{
    IDispatchMessageFormatter _inner;

    public MyMessageFormatter(IDispatchMessageFormatter formatter)
    {
        if (formatter == null)
        {
            throw new ArgumentNullException("formatter");
        }
        _inner = formatter;
    }

    public void DeserializeRequest(Message message, object[] parameters)
    {
        //加上自己的逻辑
        //轮换后的参数放到 parameters 数组中。
        _inner.DeserializeRequest(message, parameters);
    }

    public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
    {
        //加上自己的逻辑
        
        return _inner.SerializeReply(messageVersion, parameters, result);
    }
}

public class MyBehavior : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        DataContractSerializerOperationBehavior dcBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();

        if (dispatchOperation.Formatter == null)
        {
            ((IOperationBehavior)dcBehavior).ApplyDispatchBehavior(operationDescription, dispatchOperation);
        }
        dispatchOperation.Formatter = new MyMessageFormatter(dispatchOperation.Formatter);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}

SerializeReply 需要创建一个 Message,而该过程有点麻烦(需要按照 soap 及 response XSD 的格式来拼),如果能够借用 .Net 自带的 formatter 会方便很多。而 .Net 提供的 DataContractSerializerOperationFormatter 并不是 public 的,无法直接使用。因此需要利用 DataContractSerializerOperationBehavior。该 Behavior 是 WCF 运行时默认加上的,当我们没有自定义 Formatter 时,就会将 DataContractSerializerOperationFormatter 赋值给 DispatchOperation.Formatter。

注意,该 behavior 执行 ApplyDispatchBehavior 的时机往往是在最后的,因此当我们自定义的 behavior 中需要去获取 formatter 的时候,往往是 null。


当创建了自定义的 Formatter 之后,需要通过 Behavior 把它与 WCF 运行时关联起来,请参考《WCF 扩展之我见: Behaviors》。

参考资源

Custom Message Formatters

WCF Extensibility – Message Formatters

文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号