如何使用OperationContract反序列化WCF消息

发布于 2024-10-08 21:50:23 字数 2301 浏览 6 评论 0原文

我成功地构建了由 svcutil.exe 从 WSDL 生成的 WCF 客户端。使用生成的客户端代理类,我可以调用外部服务提供商的 Web 服务。我还成功地编写了一个消息检查器,因为我需要将原始 XML 请求和响应作为完整的 SOAP 消息记录到数据库中。

对于紧急情况,我还需要能够“导入”原始 XML 响应。我发现了许多关于使用 XMLSerializer 或基于消息协定反序列化 WCF 消息的提示。

但是如何根据操作契约反序列化原始 XML 响应呢?对于第一个测试,我使用记录的原始响应之一,将其保存到文件中,然后尝试将其反序列化为客户端代理中生成的响应类型。无论如何,我必须成功地从类 ClientOperation 调用 DeserializeReply()。但如何到达那里呢?

我很乐意接受任何帮助,因为我对 WCF 还很陌生...... TIA, 斯特凡

这是我在马克回答后尝试的:

  public static RatingResult DeserializeResponseFromFile(string path)
  {
     var xmlReader = XmlReader.Create(path);
     var message = Message.CreateMessage(xmlReader, int.MaxValue, MessageVersion.Soap11);
     var readerAtBodyContents = message.GetReaderAtBodyContents();
     var dcs = new DataContractSerializer(typeof(RatingResult), "RatingResponse", "http://rating.webservice.xxx.de");

     // Error in line 6 position 7. 'EndElement' 'RatingResponse' from namespace
     // 'http://rating.webservice.xxx.de' is not expected.
     // Expecting element 'commonDataField'.
     var wsResult = (RatingResult)dcs.ReadObject(readerAtBodyContents);

     return wsResult;
  }

这是记录的 XML 响应文件的一部分,我试图将其反序列化为 RatingResponse 类型:

<soapenv:Envelope xmlns:soapenv="..." xmlns:soapenc="..." xmlns:xsd="..." xmlns:xsi="...">
  <soapenv:Header soapenv:encodingStyle="..." />
  <soapenv:Body soapenv:encodingStyle="...">
    <p933:RatingResponse xmlns:p933="http://rating.webservice.xxx.de">
      <RatingReturn href="#id0" />
    </p933:RatingResponse>
    <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p878:RatingResult" xmlns:p878="http://output.rating.webservice.xxx.de">
      <commonData href="#id1" />
      <acctData href="#id2" />
      <resultData href="#id3" />
    </multiRef>
    <multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p719:RatingCommonData" xmlns:p719="http://input.rating.webservice.xxx.de">
      <requestdate xsi:type="xsd:dateTime">2010-12-24T09:45:09.531Z</requestdate>
      ...

我猜测数据协定序列化程序在反序列化 href 时遇到问题。请注意,我尝试“手动”反序列化的消息是使用我注入的消息检查器捕获的。在 Web 服务的“正常”调用中,该消息可以毫无问题地反序列化。

I succeeded in building a WCF client generated by svcutil.exe from the WSDL. Using the generated client proxy class I can call the web service of an external service supplier. I also succeeded in coding a message inspector, as I need to log both raw XML request and response as full SOAP message to the database.

For an emergency scenario I also need to be able to "import" a raw XML response. I found many hints on using XMLSerializer or deserializing WCF messages based on the message contract.

But how can I deserialize a raw XML response based on an operation contract? For a first test I use one of the logged raw responses, save it to a file and now try to deserialize it to the response type as generated in the client proxy. Somehow I must succeed in calling DeserializeReply() from class ClientOperation. But how to get there?

I happily accept any help as I'm quite new to WCF...
TIA,
Stefan

This is what I tried after Marc's answer:

  public static RatingResult DeserializeResponseFromFile(string path)
  {
     var xmlReader = XmlReader.Create(path);
     var message = Message.CreateMessage(xmlReader, int.MaxValue, MessageVersion.Soap11);
     var readerAtBodyContents = message.GetReaderAtBodyContents();
     var dcs = new DataContractSerializer(typeof(RatingResult), "RatingResponse", "http://rating.webservice.xxx.de");

     // Error in line 6 position 7. 'EndElement' 'RatingResponse' from namespace
     // 'http://rating.webservice.xxx.de' is not expected.
     // Expecting element 'commonDataField'.
     var wsResult = (RatingResult)dcs.ReadObject(readerAtBodyContents);

     return wsResult;
  }

This is part of the logged XML response file, that I'm trying to deserialize to type RatingResponse:

<soapenv:Envelope xmlns:soapenv="..." xmlns:soapenc="..." xmlns:xsd="..." xmlns:xsi="...">
  <soapenv:Header soapenv:encodingStyle="..." />
  <soapenv:Body soapenv:encodingStyle="...">
    <p933:RatingResponse xmlns:p933="http://rating.webservice.xxx.de">
      <RatingReturn href="#id0" />
    </p933:RatingResponse>
    <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p878:RatingResult" xmlns:p878="http://output.rating.webservice.xxx.de">
      <commonData href="#id1" />
      <acctData href="#id2" />
      <resultData href="#id3" />
    </multiRef>
    <multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="..." xsi:type="p719:RatingCommonData" xmlns:p719="http://input.rating.webservice.xxx.de">
      <requestdate xsi:type="xsd:dateTime">2010-12-24T09:45:09.531Z</requestdate>
      ...

I guess that the data contract serializer has problems deserializing the href's. Please note that the message I try to deserialize "by hand" was captured using my injected message inspector. In a "normal" call of the web service this message get deserialized without problems.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

老娘不死你永远是小三 2024-10-15 21:50:23

我不太明白你想问什么、想做什么......基于运营合同操作契约只是您在操作/方法调用上放置的一个属性,用于将其标记为服务方法......操作契约不执行任何远程涉及序列化或反序列化的操作。 ...您的意思是如何使用 WCF 默认序列化程序 DataContractSerializer 反序列化 XML 消息?

假设您确实想了解HOWTO:使用 DataContractSerializer 反序列化 WCF 消息,那么请尝试以下操作:如果您有来自使用默认 WCF DataContractSerializer 的服务调用的响应 XML,您应该能够像这样反序列化它(假设您的 XML 序列化响应位于 xmlResponse 变量中):

using(MemoryStream memStm = new MemoryStream())
using(StreamWriter stw = new StreamWriter(memStm))
{
   // write your response to the memory stream
   stw.Write(xmlResponse);
   stw.Flush();

   // "reset" memory stream
   memStm.Seek(0, SeekOrigin.Begin);

   // setup DataContractSerializer     
   DataContractSerializer dcs = new DataContractSerializer(typeof(YourDataType));

   // deserialize result XML into an instance of "YourDataType"
   var result = dcs.ReadObject(memStm);
}

I don't really understand what you're trying to ask and to do.... based on an operation contract ?? The operation contract is just an attribute you put on an operation / method call to mark it as a service method .... the operation contract doesn't do anything even remotely involved with serialization or deserialization..... do you mean how to deserialize an XML message using the DataContractSerializer which is the WCF default serializer??

Assuming you do really mean HOWTO: deserialize a WCF message using the DataContractSerializer, then try this: if you have the response XML from a service call that used the default WCF DataContractSerializer, you should be able to deserialize it like this (assuming you have your XML serialized response in a xmlResponse variable):

using(MemoryStream memStm = new MemoryStream())
using(StreamWriter stw = new StreamWriter(memStm))
{
   // write your response to the memory stream
   stw.Write(xmlResponse);
   stw.Flush();

   // "reset" memory stream
   memStm.Seek(0, SeekOrigin.Begin);

   // setup DataContractSerializer     
   DataContractSerializer dcs = new DataContractSerializer(typeof(YourDataType));

   // deserialize result XML into an instance of "YourDataType"
   var result = dcs.ReadObject(memStm);
}
濫情▎り 2024-10-15 21:50:23

对于将来这样做的任何人。我必须手动从 MSMSQ 中读取 WCF 消息,并从 MSMQ/WCF 消息信封中获取请求对象。操作方法如下:

根代码:

var q = new MessageQueue(@".\Private$\VishalQ;poison");

var allMessages = q.GetAllMessages().ToList();
var wcfRequests = allMessages.Select(ConvertToWcfRequest<ObjectChangedRequest>);

我的合同:

[ServiceContract]
public interface IWish
{
    [OperationContract(IsOneWay = true)]
    void ObjectChanged(ObjectChangedRequest request);
}

我的数据合同:

[DataContract(Namespace = "http://x.namespaces.x-x.com/")]
public class ObjectChangedRequest
{
    [DataMember]
    public OperationType OperationType { get; set; }
}

我的消息反序列化代码:

    /// <summary>
    /// Converts a WCF MSMQ message to a WCF request object.
    /// </summary>
    public static T ConvertToWcfRequest<T>(Message msmqMessage)
    {
        var buffer = new byte[msmqMessage.BodyStream.Length];
        msmqMessage.BodyStream.Read(buffer, 0, (int)msmqMessage.BodyStream.Length);

        var envelopeStart = FindEnvelopeStart(buffer);

        using var msmqStream = new MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
        var encodingElement  = new BinaryMessageEncodingBindingElement();
        var wcfMessage       = encodingElement.CreateMessageEncoderFactory().Encoder.ReadMessage(msmqStream, int.MaxValue);
        var document         = new XmlDocument();

        document.Load(wcfMessage.GetReaderAtBodyContents());

        var realRoot        = document.FirstChild.FirstChild;
        using var wcfStream = new MemoryStream();
        using var xmlWriter = XmlWriter.Create(wcfStream);

        realRoot.WriteTo(xmlWriter);
        xmlWriter.Flush();
        wcfStream.Seek(0, SeekOrigin.Begin);

        var wcfSerializer = new DataContractSerializer(typeof(T), realRoot.Name, "http://tempuri.org/"); //No idea why this has to be temp uri and not our namespace...

        return (T)wcfSerializer.ReadObject(wcfStream);
    }

    /// <summary>
    /// Locates the start of a WCF message within a MSMQ message.
    /// </summary>
    private static int FindEnvelopeStart(byte[] stream)
    {
        var position = 0;
        var previousByte = stream[position];

        for (position = 0; position < stream.Length; position++)
        {
            var currentByte = stream[position];

            //Some magic numbers that define the start of the WCF message envelope
            if (currentByte == 0x02 && previousByte == 0x56)
                break;

            previousByte = currentByte;
        }

        return position - 1;
    }

For anyone in the future doing this. I had to manually read a WCF message out of the MSMSQ, and get the request object out of the MSMQ/WCF message envelope. Here's how:

Root code:

var q = new MessageQueue(@".\Private$\VishalQ;poison");

var allMessages = q.GetAllMessages().ToList();
var wcfRequests = allMessages.Select(ConvertToWcfRequest<ObjectChangedRequest>);

My contract:

[ServiceContract]
public interface IWish
{
    [OperationContract(IsOneWay = true)]
    void ObjectChanged(ObjectChangedRequest request);
}

My Data Contract:

[DataContract(Namespace = "http://x.namespaces.x-x.com/")]
public class ObjectChangedRequest
{
    [DataMember]
    public OperationType OperationType { get; set; }
}

My message deserialization code:

    /// <summary>
    /// Converts a WCF MSMQ message to a WCF request object.
    /// </summary>
    public static T ConvertToWcfRequest<T>(Message msmqMessage)
    {
        var buffer = new byte[msmqMessage.BodyStream.Length];
        msmqMessage.BodyStream.Read(buffer, 0, (int)msmqMessage.BodyStream.Length);

        var envelopeStart = FindEnvelopeStart(buffer);

        using var msmqStream = new MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
        var encodingElement  = new BinaryMessageEncodingBindingElement();
        var wcfMessage       = encodingElement.CreateMessageEncoderFactory().Encoder.ReadMessage(msmqStream, int.MaxValue);
        var document         = new XmlDocument();

        document.Load(wcfMessage.GetReaderAtBodyContents());

        var realRoot        = document.FirstChild.FirstChild;
        using var wcfStream = new MemoryStream();
        using var xmlWriter = XmlWriter.Create(wcfStream);

        realRoot.WriteTo(xmlWriter);
        xmlWriter.Flush();
        wcfStream.Seek(0, SeekOrigin.Begin);

        var wcfSerializer = new DataContractSerializer(typeof(T), realRoot.Name, "http://tempuri.org/"); //No idea why this has to be temp uri and not our namespace...

        return (T)wcfSerializer.ReadObject(wcfStream);
    }

    /// <summary>
    /// Locates the start of a WCF message within a MSMQ message.
    /// </summary>
    private static int FindEnvelopeStart(byte[] stream)
    {
        var position = 0;
        var previousByte = stream[position];

        for (position = 0; position < stream.Length; position++)
        {
            var currentByte = stream[position];

            //Some magic numbers that define the start of the WCF message envelope
            if (currentByte == 0x02 && previousByte == 0x56)
                break;

            previousByte = currentByte;
        }

        return position - 1;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文