Deserializing WCF Message at Server

你。 提交于 2019-12-23 05:01:17

问题


I implemented a custom message inspector (via IDispatchMessageInspector) to intercept messages that are received on the server side of a WCF service so I can attempt to deserialize the message and apply some specific business logic. The problem I'm encountering is when I write the MessageBuffer's contents to a new MemoryStream and then try to deserialize, I get an error that says "The data at the root level is invalid. Line 1, position 1." I do know the data being passed in is valid as skipping over the inspector makes everything work fine.

Sample Code:

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        string msg = buffer.CreateMessage().ToString();

        var dc = new DataContractSerializer(typeof(Adder));

        using (var stream = new MemoryStream())
        {
            buffer.WriteMessage(stream);

            stream.Position = 0;

            //deserializing error occurs here
            var c = dc.ReadObject(stream);
        }

        return null;
    }

Here is the Adder class/interface:

    [DataContract(Name = "adder", Namespace = "http://test.com")]
public class Adder
{
    [DataMember(Name = "first")]
    public int First { get; set; }

    [DataMember(Name = "second")]
    public int Second { get; set; }
}

    [ServiceContract(Namespace = "http://test.com")]
public interface ITestSvc
{
    [OperationContract(Name = "add")]
    int Add(Adder adder);
}

Any suggestions or is there a better option for this? My main goal is to read the XML (in a deserialized object) on every WCF request that comes into my service.


回答1:


The request object contains the WCF message headers as well as the payload. You'll need to strip off the headers and then you should be able to deserialize the message body.

For example, a SOAP message would have:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>

</soap:Header>
<soap:Body>
  <!-- your payload -->
</soap:Body>

You could use XML navigation to get to the body element and then you'd deserialize that element alone.

EDIT: Actually just stumbled on this method which I think should do the trick for you:

Message.GetReaderAtBodyContents




回答2:


I just did this. Just paste all of the following code into your class, and call DeserializedResponse(). You'll need to change MyResponseObject to the name of whatever object you are trying to deserialize. Also, you will need to replace "_requestInspector.Response" with your own response xml string variable. The method GetSoapBodyInnerXml() will strip off the Soap Envelope, and only return your response xml which you wish to deserialize.

    private MyResponseObject DeserializedResponse()
    {
        var rootAttribute = new XmlRootAttribute("MyResponseObject ");
        rootAttribute.Namespace = @"http://www.company.com/MyResponseObjectNamespace";

        XmlSerializer serializer = new XmlSerializer(typeof(MyResponseObject ), rootAttribute);
        string responseSoapBodyInnerXml = GetSoapBodyInnerXml(_requestInspector.Response);
        AddXmlDeclaration(ref responseSoapBodyInnerXml);
        MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(responseSoapBodyInnerXml));
        MyResponseObject resultingResponse = (MyResponseObject )serializer.Deserialize(memStream);

        return resultingResponse;
    }

    private string GetSoapBodyInnerXml(string soapMessage)
    {
        XDocument xDoc = XDocument.Parse(soapMessage);
        XNamespace nsSoap = @"http://schemas.xmlsoap.org/soap/envelope/";
        return xDoc.Descendants(nsSoap + CONST_SoapBody).Descendants().First().ToString();
    }

    private void AddXmlDeclaration(ref string xmlString)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xmlString);

        //Create an XML declaration. 
        XmlDeclaration xmldecl;
        xmldecl = doc.CreateXmlDeclaration("1.0", "UTF-8", null);

        //Add the new node to the document.
        XmlElement root = doc.DocumentElement;
        doc.InsertBefore(xmldecl, root);

        //Return updated xmlString with XML Declaration
        xmlString = doc.InnerXml;
    }



回答3:


I ended up going the message header route, like Mike Parkhill suggested.



来源:https://stackoverflow.com/questions/13040959/deserializing-wcf-message-at-server

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!