kum*_*ama 7 c# asp.net soap web-services soap-extension
我正在尝试为我的ASP.NET Web服务的所有SOAP方法调用创建一个日志记录服务.我一直在查看 来自控制台应用程序的Log SOAP消息以及MSDN上SOAP扩展的演练(http://msdn.microsoft.com/en-us/library/s25h0swd%28v=vs.100%29.aspx)但他们似乎并没有完全覆盖它.
我不想改变SOAP消息,只需将其记录到数据库表中即可.我正在尝试做的是读取SOAP消息流,将其解析为XML,记录XML并让调用以其快乐的方式进行.但是当我读取它时,它被花费/处理掉了.我已经尝试复制流内容以不中断流.
根据演练,该ProcessMessage方法应如下所示:
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
// Write the SOAP message out to a file.
WriteOutput( message );
break;
case SoapMessageStage.BeforeDeserialize:
// Write the SOAP message out to a file.
WriteInput( message );
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
Run Code Online (Sandbox Code Playgroud)
我已经设法在BeforeDeserialize阶段期间解析流没有问题,但然后ProcessMessage在AfterSerialize阶段再次调用,然后使用流并且不再包含任何数据.
根据SOAP消息修改使用SOAP扩展(http://msdn.microsoft.com/en-us/library/esw638yk%28v=vs.100%29.aspx),SOAP调用将执行以下步骤:
服务器端接收请求消息并准备响应
- Web服务器上的ASP.NET接收SOAP消息.
- 在Web服务器上创建SOAP扩展的新实例.
- 在Web服务器上,如果这是第一次在服务器端使用此Web服务执行此SOAP扩展,则会在服务器上运行的SOAP扩展上调用GetInitializer方法.
- 调用Initialize方法.
- 调用ChainStream方法.
- 使用设置为BeforeDeserialize的 SoapMessageStage调用ProcessMessage方法.
- ASP.NET反序列化XML中的参数.
- 在SoapMessageStage设置为AfterDeserialize的情况下调用ProcessMessage方法.
- ASP.NET创建实现Web服务的类的新实例,并调用Web服务方法,传入反序列化的参数.此对象与Web服务器驻留在同一台计算机上.
- Web服务方法执行其代码,最终设置返回值和任何输出参数.
- 在SoapMessageStage设置为BeforeSerialize的情况下调用ProcessMessage方法.
- Web服务器上的ASP.NET将返回值和输出参数序列化为XML.
- 使用设置为AfterSerialize的 SoapMessageStage调用ProcessMessage方法.
- ASP.NET通过网络将SOAP响应消息发送回Web服务客户端.
正确执行步骤6并记录SOAP XML.然后,在服务器处理完呼叫之后,它应该不再做任何事情,完成它所需要的(步骤10),并返回响应(步骤13).相反,它会立即ProcessMessage在AfterSerialize阶段中再次调用,但这次流已经花费,并在我尝试记录时抛出异常.
根据演练我应该使用该ChainStream方法,它应该在上面的步骤5中运行.当我拨打电话时,它会运行两次,前BeforeDeserialize一次和前一次AfterSerialize.
我已经尝试将消息流复制到单独的流并将其用于日志记录,如果BeforeDeserialize已经运行,也设置某种状态,但问题仍然存在.
我仍然需要代码AfterSerialize来处理发送回客户端的响应.但是,如果我尝试删除我的代码AfterSerialize,只运行BeforeDeserialize' I get aHTTP 400中的代码:错误请求.
这一切都发生在实际的方法调用之前,所以我从来没有得到方法内的代码(步骤10).
我的解决方案是基于迈克布里奇的解决方案,但我不得不做一些改动.必须包含初始化程序,如果您尝试在不可用的阶段访问soap消息信息,则会抛出异常.
public class SoapLoggingExtension : SoapExtension
{
private Stream _originalStream;
private Stream _workingStream;
private static String _initialMethodName;
private static string _methodName;
private static String _xmlResponse;
/// <summary>
/// Side effects: saves the incoming stream to
/// _originalStream, creates a new MemoryStream
/// in _workingStream and returns it.
/// Later, _workingStream will have to be created
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public override Stream ChainStream(Stream stream)
{
_originalStream = stream;
_workingStream = new MemoryStream();
return _workingStream;
}
/// <summary>
/// Process soap message
/// </summary>
/// <param name="message"></param>
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
//Get soap call as a xml string
var xmlRequest = GetSoapEnvelope(_workingStream);
//Save the inbound method name
_methodName = message.MethodInfo.Name;
CopyStream(_workingStream, _originalStream);
//Log call
LogSoapRequest(xmlRequest, _methodName, LogObject.Direction.OutPut);
break;
case SoapMessageStage.BeforeDeserialize:
CopyStream(_originalStream, _workingStream);
//Get xml string from stream before it is used
_xmlResponse = GetSoapEnvelope(_workingStream);
break;
case SoapMessageStage.AfterDeserialize:
//Method name is only available after deserialize
_methodName = message.MethodInfo.Name;
LogSoapRequest(_xmlResponse, _methodName, LogObject.Direction.InPut);
break;
}
}
/// <summary>
/// Returns the XML representation of the Soap Envelope in the supplied stream.
/// Resets the position of stream to zero.
/// </summary>
private String GetSoapEnvelope(Stream stream)
{
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
String data = reader.ReadToEnd();
stream.Position = 0;
return data;
}
private void CopyStream(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
public override object GetInitializer(Type serviceType)
{
return serviceType.FullName;
}
//Never needed to use this initializer, but it has to be implemented
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
throw new NotImplementedException();
//return ((TraceExtensionAttribute)attribute).Filename;
}
public override void Initialize(object initializer)
{
if (String.IsNullOrEmpty(_methodName))
{
_initialMethodName = _methodName;
_waitForResponse = false;
}
}
private void LogSoapRequest(String xml, String methodName, LogObject.Direction direction)
{
String connectionString = String.Empty;
String callerIpAddress = String.Empty;
String ipAddress = String.Empty;
try
{
//Only log outbound for the response to the original call
if (_waitForResponse && xml.IndexOf("<" + _initialMethodName + "Response") < 0)
{
return;
}
if (direction == LogObject.Direction.InPut) {
_waitForResponse = true;
_initialMethodName = methodName;
}
connectionString = GetSqlConnectionString();
callerIpAddress = GetClientIp();
ipAddress = GetClientIp(HttpContext.Current.Request.UserHostAddress);
//Log call here
if (!String.IsNullOrEmpty(_methodName) && xml.IndexOf("<" + _initialMethodName + "Response") > 0)
{
//Reset static values to initial
_methodName = String.Empty;
_initialMethodName = String.Empty;
_waitForResponse = false;
}
}
catch (Exception ex)
{
//Error handling here
}
}
private static string GetClientIp(string ip = null)
{
if (String.IsNullOrEmpty(ip))
{
ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
}
if (String.IsNullOrEmpty(ip) || ip.Equals("unknown", StringComparison.OrdinalIgnoreCase))
{
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
}
if (ip == "::1")
ip = "127.0.0.1";
return ip;
}
}
Run Code Online (Sandbox Code Playgroud)
methodName变量用于确定我们正在等待响应的入站调用.这当然是可选的,但在我的解决方案中,我对其他web服务进行了几次调用,但我想只记录第一次调用的响应.
第二部分是您需要在web.config中添加正确的行.显然,不包括整个类类型定义是敏感的(在这个例子中,只定义了类名,它不起作用.该类从未被初始化.):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add group="High" priority="1" type="WsNs.SoapLoggingExtension, WsNs, Version=1.0.0.0, Culture=neutral" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12560 次 |
| 最近记录: |