在与不同公司的Web服务交互过程中,发现有些服务会返回无效的XML数据。这些数据之所以无效,可能是因为使用了过时的模式,或者存在一些微妙的问题,导致Microsoft的反序列化程序抛出异常。由于需要查看发送给数据,不得不绕过反序列化操作,直接访问原始的SOAP消息,而Microsoft的代码拒绝透露给Web服务。本文解释了是如何通过创建一个特定属性,将其绑定到方法调用上,从而在序列化之前查看SOAP消息,并在需要时记录消息的。
由于正在使用外部Web服务,要么必须处理Microsoft生成的代理代码,要么必须手动创建它;无论如何,需要找到返回数据的调用位置。那就是将放置专用属性的位置。如果它是一个生成的代理,那么需要查看隐藏的文件Reference.cs,它位于Web服务文件夹中。找到后,定位返回数据的主要方法。那就是需要添加属性以处理反序列化情况的位置。
属性是数据记录过程的钩子。以下是作为属性的类,具有适当的覆盖。
public class ClientSoapLoggerAttribute : SoapExtensionAttribute
{
private int m_Priority = 0;
public override Type ExtensionType
{
get { return typeof(ClientSoapLogger); }
}
public override int Priority
{
get { return m_Priority; }
set { m_Priority = value; }
}
}
SOAP扩展将以自主的方式处理SOAP消息的处理流程。目标不是破坏数据流,只是为了数据提取而查看它。
public class ClientSoapLogger : SoapExtension
{
public Stream oldStream;
public Stream newStream;
public override object GetInitializer(Type serviceType)
{
return null;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override void Initialize(object initializer)
{
return;
}
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:
LogResponse(message);
break;
case SoapMessageStage.AfterSerialize:
LogRequest(message);
break;
case SoapMessageStage.AfterDeserialize:
case SoapMessageStage.BeforeSerialize:
default:
break;
}
}
}
这是真正让头疼的步骤。如果代码在一个Web服务中(代码是一个Web服务调用另一个Web服务),那么如何将数据在SOAP处理和访问代理的代码之间传递呢?在某些情况下,如ASP.NET,可以将消息放在HttpContext中,但这对Web服务代码来说并不是一个选项。下面的代码处理了HttpContext为null的情况,将数据放在Remoting调用上下文中。
public void LogResponse(SoapMessage message)
{
Copy(oldStream, newStream);
newStream.Position = 0;
string strSOAPresponse = ExtractFromStream(newStream);
HttpContext hc = HttpContext.Current;
if (hc != null)
hc.Items.Add("SOAPResponse", strSOAPresponse);
else
System.Runtime.Remoting.Messaging.CallContext.SetData("SOAPResponse", strSOAPresponse);
newStream.Position = 0;
}
private String ExtractFromStream(Stream target)
{
if (target != null)
return (new StreamReader(target)).ReadToEnd();
return "";
}
private void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
protected virtual void DiagnoseResponseProblem()
{
HttpContext hc = HttpContext.Current;
string SoapResponse = null;
string SoapRequest = null;
if (hc != null)
{
SoapRequest = hc.Items["SOAPRequest"].ToString();
SoapResponse = hc.Items["SOAPResponse"].ToString();
}
else
{
try
{
SoapResponse = System.Runtime.Remoting.Messaging.CallContext.GetData("SOAPResponse").ToString();
SoapRequest = System.Runtime.Remoting.Messaging.CallContext.GetData("SOAPRequest").ToString();
System.Runtime.Remoting.Messaging.CallContext.FreeNamedDataSlot("SOAPResponse");
System.Runtime.Remoting.Messaging.CallContext.FreeNamedDataSlot("SOAPRequest");
}
catch (System.Exception) { }
}
}