无法使IClientMessageFormatter正常工作

3
我正在使用自定义的IClientMessageFormatter,目前它会向消息中指定的XML元素添加属性:
public class GetOrdersMessageFormatter : IClientMessageFormatter
{
    readonly IClientMessageFormatter original;

    public GetOrdersMessageFormatter(IClientMessageFormatter actual)
    {
        original = actual;
    }

    public void AddArrayNamespace(XmlNode node)
    {
        if (node != null)
        {
            var attribute = node.OwnerDocument.CreateAttribute("test");
            attribute.Value = "test";
            node.Attributes.Append(attribute);
        }
    }     

    public object DeserializeReply(Message message, object[] parameters)
    {
        return original.DeserializeReply(message, parameters);
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        Message newMessage = null;

        var message = original.SerializeRequest(messageVersion, parameters);

        if (message.Headers.Action == "urn:Mage_Api_Model_Server_HandlerAction")
        {
            var doc = new XmlDocument();

            using (var reader = message.GetReaderAtBodyContents())
            {
                doc.Load(reader);
            }

            if (doc.DocumentElement != null)
            {
                switch (doc.DocumentElement.LocalName)
                {
                    case "call":
                        AddArrayNamespace(doc.SelectSingleNode("//args"));
                        break;
                }
            }

            using (var ms = new MemoryStream())
            {
                using (var xw = XmlWriter.Create(ms))
                {
                    doc.Save(xw);                      

                    ms.Position = 0;
                    using (var xr = XmlReader.Create(ms))
                    {
                        newMessage = Message.CreateMessage(message.Version, null, xr);
                        newMessage.Headers.CopyHeadersFrom(message);
                        newMessage.Properties.CopyProperties(message.Properties);
                    }
                }
            }
        }

        return newMessage;
    }     
}

出现异常

System.ArgumentException:消息正文所使用的 XmlReader 必须定位在元素上。 参数名: reader

服务器堆栈跟踪: at System.ServiceModel.Channels.XmlReaderBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriter.WriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BodyWriterMessage.OnWriteBodyContents(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer) at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota) at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message) at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout) at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout) at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout) at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout) at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation) at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

我认为,也许需要使用message.CreateBufferedCopy()创建消息副本,并使用该副本加载XmlDocument,但这也没有起作用。 也许有人知道我做错了什么,或者知道一个做类似事情的示例(我的意思是在发送消息之前影响消息xml)。

1个回答

5
您的格式化程序存在问题,原因在于您创建了一个消息读取器和流,但这些资源在消息被消费之前就已经被释放。将创建流和读取器的"using"语句删除后,请求就可以正常通过。请参考下面的代码:
public class StackOverflow_8669406
{
    public class GetOrdersMessageFormatter : IClientMessageFormatter
    {
        readonly IClientMessageFormatter original;

        public GetOrdersMessageFormatter(IClientMessageFormatter actual)
        {
            original = actual;
        }

        public void AddArrayNamespace(XmlNode node)
        {
            if (node != null)
            {
                var attribute = node.OwnerDocument.CreateAttribute("test");
                attribute.Value = "test";
                node.Attributes.Append(attribute);
            }
        }

        public object DeserializeReply(Message message, object[] parameters)
        {
            return original.DeserializeReply(message, parameters);
        }

        public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
        {
            Message newMessage = null;

            var message = original.SerializeRequest(messageVersion, parameters);

            if (message.Headers.Action == "urn:Mage_Api_Model_Server_HandlerAction")
            {
                var doc = new XmlDocument();

                using (var reader = message.GetReaderAtBodyContents())
                {
                    doc.Load(reader);
                }

                if (doc.DocumentElement != null)
                {
                    switch (doc.DocumentElement.LocalName)
                    {
                        case "call":
                            AddArrayNamespace(doc.SelectSingleNode("//args"));
                            break;
                    }
                }

                var ms = new MemoryStream();

                XmlWriterSettings ws = new XmlWriterSettings
                {
                    CloseOutput = false,
                };

                using (var xw = XmlWriter.Create(ms, ws))
                {
                    doc.Save(xw);
                    xw.Flush();
                }

                Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));

                ms.Position = 0;
                var xr = XmlReader.Create(ms);
                newMessage = Message.CreateMessage(message.Version, null, xr);
                newMessage.Headers.CopyHeadersFrom(message);
                newMessage.Properties.CopyProperties(message.Properties);
            }

            return newMessage;
        }
    }

    [ServiceContract(Namespace = "")]
    public interface ITest
    {
        [OperationContract(Action = "urn:Mage_Api_Model_Server_HandlerAction")]
        int call(string args);
    }
    public class Service : ITest
    {
        public int call(string args)
        {
            return int.Parse(args);
        }
    }
    class MyBehavior : IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
            clientOperation.Formatter = new GetOrdersMessageFormatter(clientOperation.Formatter);
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
        }

        public void Validate(OperationDescription operationDescription)
        {
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
        foreach (OperationDescription operation in factory.Endpoint.Contract.Operations)
        {
            operation.Behaviors.Add(new MyBehavior());
        }

        ITest proxy = factory.CreateChannel();
        Console.WriteLine(proxy.call("4455"));

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接