我正试图在我当前的项目中解决一个Web服务客户端问题.我不确定服务服务器的平台(很可能是LAMP).我相信他们身边有一个错误,因为我已经消除了我的客户的潜在问题.客户端是从服务WSDL自动生成的标准ASMX类型Web引用代理.
我需要得到的是RAW SOAP消息(请求和响应)
最好的方法是什么?
我进行了以下更改web.config
以获取SOAP(请求/响应)信封.这会将所有原始SOAP信息输出到该文件trace.log
.
您可以实现SoapExtension,将完整请求和响应记录到日志文件中.然后,您可以在web.config中启用SoapExtension,这样可以轻松打开/关闭以进行调试.这是我为自己使用找到并修改的一个示例,在我的例子中,日志记录是由log4net完成的,但您可以用自己的方法替换日志方法.
public class SoapLoggerExtension : SoapExtension { private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Stream oldStream; private Stream newStream; public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return null; } public override object GetInitializer(Type serviceType) { return null; } public override void Initialize(object initializer) { } public override System.IO.Stream ChainStream(System.IO.Stream stream) { oldStream = stream; newStream = new MemoryStream(); return newStream; } public override void ProcessMessage(SoapMessage message) { switch (message.Stage) { case SoapMessageStage.BeforeSerialize: break; case SoapMessageStage.AfterSerialize: Log(message, "AfterSerialize"); CopyStream(newStream, oldStream); newStream.Position = 0; break; case SoapMessageStage.BeforeDeserialize: CopyStream(oldStream, newStream); Log(message, "BeforeDeserialize"); break; case SoapMessageStage.AfterDeserialize: break; } } public void Log(SoapMessage message, string stage) { newStream.Position = 0; string contents = (message is SoapServerMessage) ? "SoapRequest " : "SoapResponse "; contents += stage + ";"; StreamReader reader = new StreamReader(newStream); contents += reader.ReadToEnd(); newStream.Position = 0; log.Debug(contents); } void ReturnStream() { CopyAndReverse(newStream, oldStream); } void ReceiveStream() { CopyAndReverse(newStream, oldStream); } public void ReverseIncomingStream() { ReverseStream(newStream); } public void ReverseOutgoingStream() { ReverseStream(newStream); } public void ReverseStream(Stream stream) { TextReader tr = new StreamReader(stream); string str = tr.ReadToEnd(); char[] data = str.ToCharArray(); Array.Reverse(data); string strReversed = new string(data); TextWriter tw = new StreamWriter(stream); stream.Position = 0; tw.Write(strReversed); tw.Flush(); } void CopyAndReverse(Stream from, Stream to) { TextReader tr = new StreamReader(from); TextWriter tw = new StreamWriter(to); string str = tr.ReadToEnd(); char[] data = str.ToCharArray(); Array.Reverse(data); string strReversed = new string(data); tw.Write(strReversed); tw.Flush(); } private void CopyStream(Stream fromStream, Stream toStream) { try { StreamReader sr = new StreamReader(fromStream); StreamWriter sw = new StreamWriter(toStream); sw.WriteLine(sr.ReadToEnd()); sw.Flush(); } catch (Exception ex) { string message = String.Format("CopyStream failed because: {0}", ex.Message); log.Error(message, ex); } } } [AttributeUsage(AttributeTargets.Method)] public class SoapLoggerExtensionAttribute : SoapExtensionAttribute { private int priority = 1; public override int Priority { get { return priority; } set { priority = value; } } public override System.Type ExtensionType { get { return typeof (SoapLoggerExtension); } } }
然后,将以下部分添加到web.config,其中YourNamespace和YourAssembly指向SoapExtension的类和程序集:
不知道为什么用web.config或序列化程序类大惊小怪.以下代码对我有用:
XmlSerializer xmlSerializer = new XmlSerializer(myEnvelope.GetType()); using (StringWriter textWriter = new StringWriter()) { xmlSerializer.Serialize(textWriter, myEnvelope); return textWriter.ToString(); }
试试Fiddler2,它会让你检查请求和响应.值得注意的是,Fiddler同时使用http和https流量.
如果对Web引用的调用抛出异常,看起来Tim Carter的解决方案不起作用.我一直试图获得原始网络共鸣,所以我可以在抛出异常后在错误处理程序中检查它(在代码中).但是,我发现当调用抛出异常时,Tim的方法写的响应日志是空白的.我并不完全理解代码,但看起来Tim的方法在.Net已经失效并放弃了Web响应之后切入了这个过程.
我正在与一个使用低级编码手动开发Web服务的客户端合作.此时,他们将自己的内部进程错误消息作为HTML格式的消息添加到SOAP格式化响应之前的响应中.当然,自动化的.Net网络参考会对此产生影响.如果我在抛出异常后得到原始HTTP响应,我可以查找并解析混合返回HTTP响应中的任何SOAP响应,并知道他们收到的数据是否正常.
后来......
这是一个有效的解决方案,即使在执行之后(注意我只是在响应之后 - 也可以得到请求):
namespace ChuckBevitt { class GetRawResponseSoapExtension : SoapExtension { //must override these three methods public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return null; } public override object GetInitializer(Type serviceType) { return null; } public override void Initialize(object initializer) { } private bool IsResponse = false; public override void ProcessMessage(SoapMessage message) { //Note that ProcessMessage gets called AFTER ChainStream. //That's why I'm looking for AfterSerialize, rather than BeforeDeserialize if (message.Stage == SoapMessageStage.AfterSerialize) IsResponse = true; else IsResponse = false; } public override Stream ChainStream(Stream stream) { if (IsResponse) { StreamReader sr = new StreamReader(stream); string response = sr.ReadToEnd(); sr.Close(); sr.Dispose(); File.WriteAllText(@"C:\test.txt", response); byte[] ResponseBytes = Encoding.ASCII.GetBytes(response); MemoryStream ms = new MemoryStream(ResponseBytes); return ms; } else return stream; } } }
以下是在配置文件中配置它的方法:
...
"TestCallWebService"应该替换为库的名称(恰好是我正在使用的测试控制台应用程序的名称).
你真的不应该去ChainStream; 你应该能够从ProcessMessage更简单地做到:
public override void ProcessMessage(SoapMessage message) { if (message.Stage == SoapMessageStage.BeforeDeserialize) { StreamReader sr = new StreamReader(message.Stream); File.WriteAllText(@"C:\test.txt", sr.ReadToEnd()); message.Stream.Position = 0; //Will blow up 'cause type of stream ("ConnectStream") doesn't alow seek so can't reset position } }
如果您查找SoapMessage.Stream,它应该是一个只读流,您可以使用它来检查此时的数据.这是一个搞砸的原因,如果您确实读取了流,后续处理炸弹没有发现数据错误(流已结束)并且您无法将位置重置为开头.
有趣的是,如果您同时使用ChainStream和ProcessMessage两种方法,ProcessMessage方法将起作用,因为您在ChainStream中将流类型从ConnectStream更改为MemoryStream,而MemoryStream允许搜索操作.(我尝试将ConnectStream转换为MemoryStream - 不允许.)
所以.....微软应该允许在ChainStream类型上进行搜索操作,或者使SoapMessage.Stream真正成为一个只读副本.(写你的国会议员等......)
还有一点.在异常之后创建了一种检索原始HTTP响应的方法之后,我仍然没有得到完整的响应(由HTTP嗅探器确定).这是因为当开发Web服务将HTML错误消息添加到响应的开头时,它没有调整Content-Length标头,因此Content-Length值小于实际响应主体的大小.我得到的只是Content-Length值的字符数 - 其余的都没有了.显然,当.Net读取响应流时,它只读取Content-Length的字符数,并且不允许Content-Length值可能出错.这是应该的; 但是如果Content-Length标头值错误,那么获得整个响应体的唯一方法就是使用HTTP嗅探器(来自http://www.ieinspector.com的 I user HTTP Analyzer ).