【问题标题】:How do I get access to SOAP response如何访问 SOAP 响应
【发布时间】:2010-09-20 08:26:41
【问题描述】:

(如果这里有什么需要澄清/更多细节,请告诉我。)

我有一个使用 SOAP 与第三方 Web 服务交互的应用程序(C#,2.* 框架)。我针对提供的 WSDL 使用 thinktecture 的 WSCF 插件来创建客户端实现。由于我无法控制的原因,SOAP 消息交换使用 WSE2.0 来保证安全性(必须修改 Thinctecture 实现以包含 WSE2.0 参考)。除了“普通”数据包之外,我还附加了一个存储的 X509 证书和一个来自先前调用不同 Web 服务的二进制安全令牌。我们正在使用某种 SSL 加密——我不知道细节。

所有必要的序列化/反序列化都包含在 Web 服务客户端中——这意味着在调用客户端后将控制权返回给我时,我无法使用 SOAP 响应中包含的整个 XML 字符串——只有反序列化的组件。不要误会 - 我认为这很好,因为这意味着我不必自己做。

但是,为了让我有一些值得存储/存档的东西,我必须在根元素处重新序列化数据。这似乎是一种资源浪费,因为我的结果是在 SOAP 响应中。

现在我的问题是: 如何访问 SOAP 响应的“清晰”版本,这样我就不必重新序列化所有内容以进行存储/归档?

编辑 - 我的应用程序是一个“无格式”的 Windows 应用程序,作为网络服务运行 - 由 WebsphereMQ 客户端触发器监视器触发。我不认为 ASP.NET 解决方案会适用。

编辑 - 由于目前的共识是我的应用程序是否是 ASP.NET 并不重要,所以我将尝试 CodeMelt(以及扩展 Chris 的)解决方案。

【问题讨论】:

    标签: c# xml web-services soap wse2.0


    【解决方案1】:

    您可以利用现有 WSE2.0 框架中的 SoapExtension 来拦截来自服务器的响应。

    public class MyClientSOAPExtension : SoapExtension
    {
    
         Stream oldStream;
         Stream newStream;
    
         // Save the Stream representing the SOAP request or SOAP response into
         // a local memory buffer.
         public override Stream ChainStream( Stream stream )
         {
                oldStream = stream;
                newStream = new MemoryStream();
                return newStream;
         }
    
        public override void ProcessMessage(SoapMessage message)
        {
           switch (message.Stage)
            {
                case SoapMessageStage.BeforeDeserialize:
                    // before the XML deserialized into object.
                    break;
                case SoapMessageStage.AfterDeserialize:
                    break;        
                case SoapMessageStage.BeforeSerialize:
                    break;
                case SoapMessageStage.AfterSerialize:
                    break;            
                default:
                    throw new Exception("Invalid stage...");
            }       
        }
    }
    

    在 SoapMessageStage.BeforeDeserialize 阶段, 您可以从 oldstream 读取您想要的预期数据(例如使用 XmlReader)。 然后将预期的数据存储在某个地方供您自己使用,并且您还需要 将旧流数据转发到新流以供 Web 服务后期使用数据,例如将 XML 反序列化为对象。

    The sample of logging all the traffic for the web service from MSDN

    【讨论】:

    • 好的 - 这与 Chris 上面发布的链接相同。让我警惕的是“ASP.NET”。由于到目前为止的共识是我的应用程序是否是 ASP.NET 并不重要,所以我会试一试。
    • 是的,只要它在 dot net 中用于消费服务。
    • @codemeit 我不知道我是否遗漏了什么,但是这个类不应该编译,因为它没有实现抽象SoapExtension签名GetInitializer(Type serviceType)GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)Initialize(object initializer).
    【解决方案2】:

    这是一个示例,您可以使用 Visual Studio 网络参考来设置 http://footballpool.dataaccess.eu/data/info.wso?WSDL

    基本上,您必须在 web 服务调用链中插入一个 XmlReader spyer,它将重建原始 XML。

    我相信这种方式比使用 SoapExtensions 更简单。

    解决方案的灵感来自http://orbinary.com/blog/2010/01/getting-the-raw-soap-xml-sent-via-soaphttpclientprotocol/

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Net;
    using System.IO;
    using System.Reflection;
    using System.Xml;
    
    
    namespace ConsoleApplication1 {
    
        public class XmlReaderSpy : XmlReader {
            XmlReader _me;
            public XmlReaderSpy(XmlReader parent) {
                _me = parent;
            }
    
            /// <summary>
            /// Extracted XML.
            /// </summary>
            public string Xml;
    
            #region Abstract method that must be implemented
            public override XmlNodeType NodeType {
                get {
    
                    return _me.NodeType;
                }
            }
    
            public override string LocalName {
                get {
                    return _me.LocalName;
                }
            }
    
            public override string NamespaceURI {
                get {
                    return _me.NamespaceURI;
                }
            }
    
            public override string Prefix {
                get {
                    return _me.Prefix;
                }
            }
    
            public override bool HasValue {
                get { return _me.HasValue; }
            }
    
            public override string Value {
                get { return _me.Value; }
            }
    
            public override int Depth {
                get { return _me.Depth; }
            }
    
            public override string BaseURI {
                get { return _me.BaseURI; }
            }
    
            public override bool IsEmptyElement {
                get { return _me.IsEmptyElement; }
            }
    
            public override int AttributeCount {
                get { return _me.AttributeCount; }
            }
    
            public override string GetAttribute(int i) {
                return _me.GetAttribute(i);
            }
    
            public override string GetAttribute(string name) {
                return _me.GetAttribute(name);
            }
    
            public override string GetAttribute(string name, string namespaceURI) {
                return _me.GetAttribute(name, namespaceURI);
            }
    
            public override void MoveToAttribute(int i) {
                _me.MoveToAttribute(i);
            }
    
            public override bool MoveToAttribute(string name) {
                return _me.MoveToAttribute(name);
            }
    
            public override bool MoveToAttribute(string name, string ns) {
                return _me.MoveToAttribute(name, ns);
            }
    
            public override bool MoveToFirstAttribute() {
                return _me.MoveToFirstAttribute();
            }
    
            public override bool MoveToNextAttribute() {
                return _me.MoveToNextAttribute();
            }
    
            public override bool MoveToElement() {
                return _me.MoveToElement();
            }
    
            public override bool ReadAttributeValue() {
                return _me.ReadAttributeValue();
            }
    
            public override bool Read() {
                bool res = _me.Read();
    
                Xml += StringView();
    
    
                return res;
            }
    
            public override bool EOF {
                get { return _me.EOF; }
            }
    
            public override void Close() {
                _me.Close();
            }
    
            public override ReadState ReadState {
                get { return _me.ReadState; }
            }
    
            public override XmlNameTable NameTable {
                get { return _me.NameTable; }
            }
    
            public override string LookupNamespace(string prefix) {
                return _me.LookupNamespace(prefix);
            }
    
            public override void ResolveEntity() {
                _me.ResolveEntity();
            }
    
            #endregion
    
    
            protected string StringView() {
                string result = "";
    
                if (_me.NodeType == XmlNodeType.Element) {
                    result = "<" + _me.Name;
    
                    if (_me.HasAttributes) {
                        _me.MoveToFirstAttribute();
                        do {
                            result += " " + _me.Name + "=\"" + _me.Value + "\"";
                        } while (_me.MoveToNextAttribute());
    
                        //Let's put cursor back to Element to avoid messing up reader state.
                        _me.MoveToElement();
                    }
    
                    if (_me.IsEmptyElement) {
                        result += "/";
                    }
    
                    result += ">";
                }
    
                if (_me.NodeType == XmlNodeType.EndElement) {
                    result = "</" + _me.Name + ">";
                }
    
                if (_me.NodeType == XmlNodeType.Text || _me.NodeType == XmlNodeType.Whitespace) {
                    result = _me.Value;
                }
    
    
    
                if (_me.NodeType == XmlNodeType.XmlDeclaration) {
                    result = "<?"  + _me.Name + " " +   _me.Value + "?>";
                }
    
                return result;
    
            }
        }
    
        public class MyInfo : ConsoleApplication1.eu.dataaccess.footballpool.Info {             
    
            protected XmlReaderSpy _xmlReaderSpy;
    
            public string Xml {
                get {
                    if (_xmlReaderSpy != null) {
                        return _xmlReaderSpy.Xml;
                    }
                    else {
                        return "";
                    }
                }
            }
    
    
            protected override XmlReader GetReaderForMessage(System.Web.Services.Protocols.SoapClientMessage message, int bufferSize) {          
                XmlReader rdr = base.GetReaderForMessage(message, bufferSize);
                _xmlReaderSpy = new XmlReaderSpy((XmlReader)rdr);
                return _xmlReaderSpy;
            }
    
        }
    
        class Program {
            static void Main(string[] args) {
    
                MyInfo info = new MyInfo();
                string[] rest = info.Cities();
    
                System.Console.WriteLine("RAW Soap XML response :\n"+info.Xml);
                System.Console.ReadLine();
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      旧线程,但如果其他人今天想要这样做:这些利用 SoapExtension 或创建“间谍”类的想法很棒,但不适用于 .NET Core。

      @mting923 建议使用 IClientMessageInspector 方法在 .NET Core 3.1 中有效;见这里:Get SOAP Message before sending it to the WebService in .NET

      生成的 SOAP 代理类在底层仍然只是一个 WCF 客户端,因此 IClientMessageInspector 方法很有效,即使对于调用旧 SOAP Web 服务的 .NET Core Azure 函数也是如此。以下适用于 .NET Core 3.1 Azure 函数:

      public class SoapMessageInspector : IClientMessageInspector
      {
          public string LastRequestXml { get; private set; }
          public string LastResponseXml { get; private set; }
      
          public object BeforeSendRequest(ref Message request, IClientChannel channel)
          {
              LastRequestXml = request.ToString();
              return request;
          }
      
          public void AfterReceiveReply(ref Message reply, object correlationState)
          {
              LastResponseXml = reply.ToString();
          }
      }
      
      public class SoapInspectorBehavior : IEndpointBehavior
      {
          private readonly SoapMessageInspector inspector_ = new SoapMessageInspector();
      
          public string LastRequestXml => inspector_.LastRequestXml;
          public string LastResponseXml => inspector_.LastResponseXml;
      
          public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
          {
          }
      
          public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
          {
          }
      
          public void Validate(ServiceEndpoint endpoint)
          {
          }
      
          public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
          {
              clientRuntime.ClientMessageInspectors.Add(inspector_);
          }
      }
      

      然后可以这样设置:

          var client = new ServiceClient();
          var soapInspector = new SoapInspectorBehavior();
          client.Endpoint.EndpointBehaviors.Add(soapInspector);
      

      在客户端代理上调用 Web 服务调用后,soapInspector.LastRequestXmlsoapInspector.LastResponseXml 将包含原始 SOAP 请求和响应(作为字符串)。

      【讨论】:

      • 链接可能会回答这个问题,但链接可能会失效。因此,请在此处添加链接页面中的相关信息,使其具有持久性和可搜索性。
      • 此处无法捕获协议或网络异常
      【解决方案4】:

      受 jfburdet 的启发,我想看看是否可以在流/字节级别直接拦截而不是重构 XML。它是!见以下代码:

      using System;
      using System.Collections.Generic;
      using System.IO;
      using System.Linq;
      using System.Text;
      using System.Web.Services.Protocols;
      using System.Xml;
      
      using Test.MyWebReference;
      
      namespace Test {
          /// <summary>
          /// Adds the ability to retrieve the SOAP request/response.
          /// </summary>
          public class ServiceSpy : OriginalService {
              private StreamSpy writerStreamSpy;
              private XmlTextWriter xmlWriter;
      
              private StreamSpy readerStreamSpy;
              private XmlTextReader xmlReader;
      
              public MemoryStream WriterStream {
                  get { return writerStreamSpy == null ? null : writerStreamSpy.ClonedStream; }
              }
      
              public XmlTextWriter XmlWriter {
                  get { return xmlWriter; }
              }
      
              public MemoryStream ReaderStream {
                  get { return readerStreamSpy == null ? null : readerStreamSpy.ClonedStream; }
              }
      
              public XmlTextReader XmlReader {
                  get { return xmlReader; }
              }
      
              protected override void Dispose(bool disposing) {
                  base.Dispose(disposing);
                  DisposeWriterStreamSpy();
                  DisposeReaderStreamSpy();
              }
      
              protected override XmlWriter GetWriterForMessage(SoapClientMessage message, int bufferSize) {
                  // Dispose previous writer stream spy.
                  DisposeWriterStreamSpy();
      
                  writerStreamSpy = new StreamSpy(message.Stream);
                  // XML should always support UTF8.
                  xmlWriter = new XmlTextWriter(writerStreamSpy, Encoding.UTF8);
      
                  return xmlWriter;
              }
      
              protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize) {
                  // Dispose previous reader stream spy.
                  DisposeReaderStreamSpy();
      
                  readerStreamSpy = new StreamSpy(message.Stream);
                  xmlReader = new XmlTextReader(readerStreamSpy);
      
                  return xmlReader;
              }
      
              private void DisposeWriterStreamSpy() {
                  if (writerStreamSpy != null) {
                      writerStreamSpy.Dispose();
                      writerStreamSpy.ClonedStream.Dispose();
                      writerStreamSpy = null;
                  }
              }
      
              private void DisposeReaderStreamSpy() {
                  if (readerStreamSpy != null) {
                      readerStreamSpy.Dispose();
                      readerStreamSpy.ClonedStream.Dispose();
                      readerStreamSpy = null;
                  }
              }
      
              /// <summary>
              /// Wrapper class to clone read/write bytes.
              /// </summary>
              public class StreamSpy : Stream {
                  private Stream wrappedStream;
                  private long startPosition;
                  private MemoryStream clonedStream = new MemoryStream();
      
                  public StreamSpy(Stream wrappedStream) {
                      this.wrappedStream = wrappedStream;
                      startPosition = wrappedStream.Position;
                  }
      
                  public MemoryStream ClonedStream {
                      get { return clonedStream; }
                  }
      
                  public override bool CanRead {
                      get { return wrappedStream.CanRead; }
                  }
      
                  public override bool CanSeek {
                      get { return wrappedStream.CanSeek; }
                  }
      
                  public override bool CanWrite {
                      get { return wrappedStream.CanWrite; }
                  }
      
                  public override void Flush() {
                      wrappedStream.Flush();
                  }
      
                  public override long Length {
                      get { return wrappedStream.Length; }
                  }
      
                  public override long Position {
                      get { return wrappedStream.Position; }
                      set { wrappedStream.Position = value; }
                  }
      
                  public override int Read(byte[] buffer, int offset, int count) {
                      long relativeOffset = wrappedStream.Position - startPosition;
                      int result = wrappedStream.Read(buffer, offset, count);
                      if (clonedStream.Position != relativeOffset) {
                          clonedStream.Position = relativeOffset;
                      }
                      clonedStream.Write(buffer, offset, result);
                      return result;
                  }
      
                  public override long Seek(long offset, SeekOrigin origin) {
                      return wrappedStream.Seek(offset, origin);
                  }
      
                  public override void SetLength(long value) {
                      wrappedStream.SetLength(value);
                  }
      
                  public override void Write(byte[] buffer, int offset, int count) {
                      long relativeOffset = wrappedStream.Position - startPosition;
                      wrappedStream.Write(buffer, offset, count);
                      if (clonedStream.Position != relativeOffset) {
                          clonedStream.Position = relativeOffset;
                      }
                      clonedStream.Write(buffer, offset, count);
                  }
      
                  public override void Close() {
                      wrappedStream.Close();
                      base.Close();
                  }
      
                  protected override void Dispose(bool disposing) {
                      if (wrappedStream != null) {
                          wrappedStream.Dispose();
                          wrappedStream = null;
                      }
                      base.Dispose(disposing);
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        MSDN Library 包含用于获取请求和响应的 XML 的示例代码,您可以使用它来归档它。显然,由于示例将数据存储在文本文件中,因此您必须进行一些更改,但这并不太复杂。

        【讨论】:

        • 对不起 - 我应该包括我没有使用 ASP.NET - 它在编辑中
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-25
        相关资源
        最近更新 更多