And*_*rry 92 asp.net proxy soap web-reference asmx
我正试图在我当前的项目中解决一个Web服务客户端问题.我不确定服务服务器的平台(很可能是LAMP).我相信他们身边有一个错误,因为我已经消除了我的客户的潜在问题.客户端是从服务WSDL自动生成的标准ASMX类型Web引用代理.
我需要得到的是RAW SOAP消息(请求和响应)
最好的方法是什么?
小智 129
我进行了以下更改web.config以获取SOAP(请求/响应)信封.这会将所有原始SOAP信息输出到该文件trace.log.
<system.diagnostics>
<trace autoflush="true"/>
<sources>
<source name="System.Net" maxdatasize="1024">
<listeners>
<add name="TraceFile"/>
</listeners>
</source>
<source name="System.Net.Sockets" maxdatasize="1024">
<listeners>
<add name="TraceFile"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
initializeData="trace.log"/>
</sharedListeners>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
</switches>
</system.diagnostics>
Run Code Online (Sandbox Code Playgroud)
duc*_*rth 34
您可以实现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); }
}
}
Run Code Online (Sandbox Code Playgroud)
然后,将以下部分添加到web.config,其中YourNamespace和YourAssembly指向SoapExtension的类和程序集:
<webServices>
<soapExtensionTypes>
<add type="YourNamespace.SoapLoggerExtension, YourAssembly"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
Run Code Online (Sandbox Code Playgroud)
小智 27
不知道为什么用web.config或序列化程序类大惊小怪.以下代码对我有用:
XmlSerializer xmlSerializer = new XmlSerializer(myEnvelope.GetType());
using (StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, myEnvelope);
return textWriter.ToString();
}
Run Code Online (Sandbox Code Playgroud)
如果对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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是在配置文件中配置它的方法:
<configuration>
...
<system.web>
<webServices>
<soapExtensionTypes>
<add type="ChuckBevitt.GetRawResponseSoapExtension, TestCallWebService"
priority="1" group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
Run Code Online (Sandbox Code Playgroud)
"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
}
}
Run Code Online (Sandbox Code Playgroud)
如果您查找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 ).