无法使IClientMessageFormatter工作

时间:2011-12-29 15:00:47

标签: c# wcf

我正在使用自定义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必须位于元素上。   参数名称:阅读器

     

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

我想,可能我需要使用message.CreateBufferedCopy()创建消息副本并使用该副本加载XmlDocument,但这也没有用。 也许有人知道我做错了什么,或者知道例子,这是做同样的事情(我的意思是在发送之前影响消息xml)。

1 个答案:

答案 0 :(得分:3)

格式化程序中的问题是您正在创建一条带有阅读器(和流)的消息,这些消息在消息被消耗之前被丢弃。在创建流和阅读器后删除“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();
    }
}