如何访问SOAP响应

(如果有任何需要澄清/更多细节请告诉我。)

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

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

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

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

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

编辑 – 由于到目前为止的共识是,我的应用程序是否是ASP.NET无关紧要,然后我将给CodeMelt(以及扩展Chris的)解决方案一个镜头。

您可以从现有的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反序列化为对象。

从MSDN记录Web服务的所有流量的示例

下面是一个示例,您可以使用Visual Studio Web引用设置http://footballpool.dataaccess.eu/data/info.wso?WSDL

基本上,您必须在webservice调用链中插入一个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; } ///  /// Extracted XML. ///  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 = ""; } if (_me.NodeType == XmlNodeType.Text || _me.NodeType == XmlNodeType.Whitespace) { result = _me.Value; } if (_me.NodeType == XmlNodeType.XmlDeclaration) { result = ""; } 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(); } } } 

受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 { ///  /// Adds the ability to retrieve the SOAP request/response. ///  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; } } ///  /// Wrapper class to clone read/write bytes. ///  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); } } } } 

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