【问题标题】:Deserializing WCF Message at Server在服务器上反序列化 WCF 消息
【发布时间】:2012-10-23 23:41:47
【问题描述】:

我实现了一个自定义消息检查器(通过 IDispatchMessageInspector)来拦截在 WCF 服务的服务器端接收到的消息,因此我可以尝试反序列化消息并应用一些特定的业务逻辑。我遇到的问题是,当我将 MessageBuffer 的内容写入新的 MemoryStream 然后尝试反序列化时,我收到一条错误消息,提示“根级别的数据无效。第 1 行,第 1 行。”我确实知道传入的数据是有效的,因为跳过检查器会使一切正常。

示例代码:

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

这是 Adder 类/接口:

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

有什么建议或有更好的选择吗?我的主要目标是读取进入我的服务的每个 WCF 请求的 XML(在反序列化对象中)。

【问题讨论】:

  • 你为什么要自己反序列化它?你有什么特别想解决的问题吗?
  • 错误消息表明它可能是由于 NameSpace
  • 这主要是出于懒惰。我正在使用一个自定义安全主体,该主体将基于每条消息中发送的数据的某些部分。我希望在收到消息时拦截它,设置 ThreadPrincipal 然后所有授权都可以自动工作。总体而言,这比在每个方法调用上手动设置更容易。
  • 我过去做过类似的事情,但我总是从消息头中提取凭据,所以实际上并没有尝试在我的消息检查器中反序列化有效负载。
  • 我不反对标头,但其中一项主要工作是确保可以从其他客户端发送相同的标头值,例如 Android/iOS 进行调用。假设他们可以,我将转到标题并完成此操作!

标签: c# wcf deserialization


【解决方案1】:

请求对象包含 WCF 消息标头以及有效负载。您需要去掉标头,然后您应该能够反序列化消息正文。

例如,一条 SOAP 消息将具有:

<?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>

您可以使用 XML 导航来访问 body 元素,然后单独反序列化该元素。

编辑: 实际上只是偶然发现了这种我认为应该为您解决问题的方法:

Message.GetReaderAtBodyContents

【讨论】:

  • 我尝试通过内置的 XML 查看器以这种方式查看消息,并且正文应该在哪里显示“...流...”,这是我以前从未见过的。我正在使用 wsHttpBinding,所以它应该立即发布整个消息,对吧?以前见过这个吗?
  • Message.GetReaderAtBodyContents 也给了我同样的错误,Message.GetBody 也是如此。这就是我如此困惑的原因。
  • 您是否检查过 XML 以确保它看起来符合您的预期?您的客户可能会发送一些意想不到的东西吗?
  • 从使用“msg”变量(如我上面的代码所示)和使用内置 XML 查看器的角度来看。那是我看到 ... 流 ... 的地方,但由于它正在执行常规 wsHttpBinding,我假设它不是流,应该是整个消息, 对?以前见过那个吗?
【解决方案2】:

我刚做了这个。只需将以下所有代码粘贴到您的类中,然后调用 DeserializedResponse()。您需要将 MyResponseObject 更改为您尝试反序列化的任何对象的名称。此外,您需要将“_requestInspector.Response”替换为您自己的响应 xml 字符串变量。 GetSoapBodyInnerXml() 方法将剥离 Soap Envelope,只返回您希望反序列化的响应 xml。

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

【讨论】:

  • 请注意...我这样做只是为了测试为什么 WCF 没有自动将 WebService 引用返回的响应 xml 字符串反序列化到响应对象中。 WCF 将空值返回到我的响应对象中,因此我必须从请求检查器捕获响应消息以尝试反序列化自己。我希望看到关于反序列化失败原因的错误,但我的手动反序列化工作并解决了我的问题。
【解决方案3】:

就像 Mike Parkhill 建议的那样,我最终选择了消息头路由。

【讨论】:

    猜你喜欢
    • 2011-09-09
    • 2019-09-09
    • 2014-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多