简体   繁体   中英

Can't get IClientMessageFormatter to work

I'm having custom IClientMessageFormatter, that currently adds attribute to specified xml element in a message:

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;
    }     
}

It gives exception

System.ArgumentException : The XmlReader used for the body of the message must be positioned on an element. Parameter name: reader

Server stack trace: 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.HttpChannelFa ctory.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)

I thought, that may be I need to create message copy using message.CreateBufferedCopy() and use that copy to load XmlDocument, but that didn't worked either. May be someone knows what I'm doing wrong, or knows example, which is doing pretty much same thing (I mean affecting message xml before sending it).

The problem in your formatter is that you're creating a message with a reader (and a stream) which are being disposed before the message is consumed. After removing the "using" statement from the creation of the stream and the reader, the request goes through - see code below.

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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM