SoapExtension.ChainStream 方法:
当在派生类中被重写时,允许 SOAP 扩展访问包含 SOAP 请求或响应的内存缓冲区。
命名空间:System.Web.Services.Protocols 程序集:System.Web.Services(在 system.web.services.dll 中)
ChainStream 确保具有最高优先级的 SOAP 扩展可以修改与通过网络发送或返回的 SOAP 消息最接近的实际数据。
SOAP 扩展应保存传入 ChainStream 的 Stream 和从 ChainStream 返回的 Stream 的引用。如果将 SOAP 扩展配置为与 XML Web services 方法一起运行,则传递到 ChainStream 中的 Stream 在 BeforeDeserializeSoapMessageStage 阶段包含已序列化的 SOAP 请求。同样,当发生序列化时,从 ChainStream 返回的 Stream 引用被写入,从而在 AfterSerializeSoapMessageStage 中包含已序列化的 SOAP 响应。
public override Stream ChainStream( Stream stream ){
oldStream = stream;
newStream = new MemoryStream();
return newStream;
}
SoapMessageStage 枚举:
成员名称 | 说明 | |
---|---|---|
AfterDeserialize | 恰好在将 SoapMessage 从 SOAP 消息反序列化到对象之后的阶段。
在 SoapClientMessage 处理过程中,AfterDeserialize 阶段发生在 SOAP 消息(包含来自 XML Web services 方法调用的响应)反序列化到对象之后,但在客户端接收到反序列化结果之前。 在 SoapServerMessage 处理过程中,AfterDeserialize 阶段出现在这种情形下:在包含 SOAP 消息的网络请求(表示已将 XML Web services 方法调用反序列化为对象)之后,对对象执行方法(表示 XML Web services 方法已调用)之前。 |
|
AfterSerialize | 恰好在序列化 SoapMessage 之后,但在通过网络发送 SOAP 消息之前的阶段。
在 SoapClientMessage 处理过程中,AfterSerialize 阶段发生在客户端调用 XML Web services 方法并且参数序列化到 XML 之后,但在包含此 XML 的 SOAP 消息被通过网络发送之前。 在 SoapServerMessage 处理过程中,AfterSerialize 阶段发生在 XML Web services 方法返回并且所有返回值都序列化到 XML 之后,但在包含此 XML 的 SOAP 消息被通过网络发送之前。 |
|
BeforeDeserialize | 恰好在将 SoapMessage 从通过网络发送的 SOAP 消息反序列化到对象之前的阶段。
在 SoapClientMessage 处理过程中,BeforeDeserialize 阶段发生在接收到来自 XML Web services 方法调用的网络响应之后,但在包含 SOAP 消息的响应反序列化到对象之前。 在 SoapServerMessage 处理过程中,BeforeDeserialize 阶段发生在 Web 服务器接收到网络请求(包含 XML Web services 方法调用的 SOAP 消息)之后,但在 SOAP 消息反序列化到对象之前。 |
|
BeforeSerialize | 恰好在序列化 SoapMessage 之前的阶段。
在 SoapClientMessage 处理过程中,BeforeSerialize 阶段发生在客户端调用 XML Web services 方法之后,但在该调用被序列化之前。 在 SoapServerMessage 处理过程中,BeforeSerialize 阶段发生在对 XML Web services 方法的调用返回之后,但在将返回值序列化并通过网络发送回客户端之前。 |
// Process the SOAP message received and write to log file.
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput( message );
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput( message );
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
LogicalMethodInfo 类:
表示 XML Web services 方法的属性 (Attribute) 和元数据。无法继承此类。
LogicalMethodInfo 主要由 SOAP 扩展用来询问 SOAP 扩展被配置为与之一起运行的 XML Web services 方法的详细信息。根据 SOAP 扩展的配置情况,它可以找到有关采用 LogicalMethodInfo 的 SoapExtension 的 GetInitializer 方法中的 XML Web services 方法的详细信息。LogicalMethodInfo 通过使用 GetCustomAttributes 属性 (Property) 访问应用于 XML Web services 方法的 Parameters 属性 (Property) 和任何自定义属性 (Attribute) 来提供诸如 XML Web services 方法参数等详细信息。
// Process the SOAP message received and write to log file.
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
WriteOutput((SoapServerMessage)message);
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput((SoapServerMessage)message);
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
} // Write the contents of the incoming SOAP message to the log file.
public void WriteInput(SoapServerMessage message)
{
// Utility method to copy the contents of one stream to another.
Copy(oldStream, newStream);
FileStream myFileStream = new FileStream(filename, FileMode.Append, FileAccess.Write);
StreamWriter myStreamWriter = new StreamWriter(myFileStream);
myStreamWriter.WriteLine("================================== Request at "
+ DateTime.Now);
myStreamWriter.WriteLine("The method that has been invoked is : ");
myStreamWriter.WriteLine("\t" + message.MethodInfo.Name);
myStreamWriter.WriteLine("The contents of the SOAP envelope are : ");
myStreamWriter.Flush();
newStream.Position = ;
Copy(newStream, myFileStream);
myFileStream.Close();
newStream.Position = ;
} // Write the contents of the outgoing SOAP message to the log file.
public void WriteOutput(SoapServerMessage message)
{
newStream.Position = ;
FileStream myFileStream = new FileStream(filename, FileMode.Append, FileAccess.Write);
StreamWriter myStreamWriter = new StreamWriter(myFileStream);
myStreamWriter.WriteLine("---------------------------------- Response at "
+ DateTime.Now);
myStreamWriter.Flush();
// Utility method to copy the contents of one stream to another.
Copy(newStream, myFileStream);
myFileStream.Close();
newStream.Position = ;
Copy(newStream, oldStream);
}
SoapExtensionAttribute 类:
当在派生类中重写时,指定 SOAP 扩展应该与 XML Web services 方法一起运行。
使用 ASP.NET 创建的 XML Web services 方法可以配置为与 SOAP 扩展一起运行,方法是将一项属性应用于该 XML Web services 方法。将自定义扩展属性添加到 XML Web services 方法或客户端代理类中的方法后,ASP.NET 将在适当的时间调用关联的扩展。扩展属性是从 SoapExtensionAttribute 派生的自定义属性类。派生属性必须重写 ExtensionType 属性以返回与该属性关联的扩展的类型。
下面的 TraceExtensionAttribute 类从 SoapExtensionAttribute 派生,以支持将该属性应用于 XML Web services 方法或 XML Web services 客户端代理类中的方法。当应用于上述两种方法中的任意一个时,TraceExtension SOAP 扩展将与该方法一起运行。
// Create a SoapExtensionAttribute for a SOAP extension that can be
// applied to an XML Web service method.
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute { private string myFilename;
private int myPriority; // Set the name of the log file were SOAP messages will be stored.
public TraceExtensionAttribute() : base()
{
myFilename = "C:\\logClient.txt";
} // Return the type of 'TraceExtension' class.
public override Type ExtensionType
{
get
{
return typeof(TraceExtension);
}
} // User can set priority of the 'SoapExtension'.
public override int Priority
{
get
{
return myPriority;
}
set
{
myPriority = value;
}
} public string Filename
{
get
{
return myFilename;
}
set
{
myFilename = value;
}
}
}
【摘】http://www.cnblogs.com/wobushixiaocai/archive/2008/04/11/1148196.html
1、SOAP扩展是在SoapMessage (SoapClientMessage和SoapServerMessage类型)传输过程中对其SOAP消息处理过程的拦截器,其可以在SoapMessage的四个阶段进行拦截并且插入代码,这四个阶段分别是BeforeSerialize"AfterSerialize"BeforeDeserialize和AfterDeserialize。
2、 在整个WebService访问过程中,ChainStream一共执行四次,在客户端执行两次,在服务器端执行两次。ChainStream的调用是按照Soap扩展的高优先级到低优先级的调用顺序依次调用的。
3、 每次SoapMessage的Stage状态发生改变时都会调用SoapExtension的ProcessMessage。所以这个函数一共执行8次。其中需要特别注意的是,不同优先级的Soap扩展在Soap消息传输的不同阶段其调用顺序是不一样的,具体可以分为两个阶段:即Serialize阶段,其调用是按照Soap扩展的优先级从低到高执行的,在Deserialize阶段,其调用是按照Soap扩展优先级从高到低执行的。
项目实践:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services.Protocols;
using System.IO;
using System.Xml;
using BillingWS.Common; namespace BillingWS.BusinessLogic
{
public class TraceExtension : SoapExtension
{
private Stream oldStream;
private Stream newStream; private string category;
private string methodName; private XmlDocument xmlRequest;
/// <summary>
/// Gets the outgoing XML request sent to PayPal
/// </summary>
private XmlDocument XmlRequest
{
get { return xmlRequest; }
} private XmlDocument xmlResponse;
/// <summary>
/// Gets the incoming XML response sent from PayPal
/// </summary>
private XmlDocument XmlResponse
{
get { return xmlResponse; }
} /// <summary>
/// Save the Stream representing the SOAP request
/// or SOAP response into a local memory buffer.
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public override Stream ChainStream(Stream stream)
{
oldStream = stream;
newStream = new MemoryStream();
return 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)
{ } public override void ProcessMessage(SoapMessage message)
{
try
{
string serviceName = message.MethodInfo.DeclaringType.Name;
category = Common.PublicEnums.WSCallSource.Zuora.ToString(); switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break; case SoapMessageStage.AfterSerialize:
xmlRequest = GetSoapEnvelope(newStream);
CopyStream(newStream, oldStream);
break; case SoapMessageStage.BeforeDeserialize:
CopyStream(oldStream, newStream);
xmlResponse = GetSoapEnvelope(newStream); if (serviceName.IndexOf("ZuoraService") != -)
{
methodName = message.MethodInfo.Name;
LogMessage();
}
break; case SoapMessageStage.AfterDeserialize:
break;
} }
catch (Exception ex)
{
ZuoraException exception = new ZuoraException(PublicEnums.ExceptionType.LogTraceFailed, "ProcessMessage Failed:" + ex.Message, ex);
exception.Log();
}
} #region Method
public void LogMessage()
{
try
{
HttpContext content = HttpContext.Current;
if (content != null && content.Session != null)
{
if (content.Session[PublicConstants.SESSION_TRANSACTIONLOGID] != null)
{
Guid transactionId = new Guid(content.Session[PublicConstants.SESSION_TRANSACTIONLOGID].ToString()); TransactionConverter converter = new TransactionConverter(transactionId, category, methodName, xmlRequest, xmlResponse);
converter.LogDB();
}
}
}
catch (Exception ex)
{
ZuoraException exception = new ZuoraException(PublicEnums.ExceptionType.LogTraceFailed, "LogMessage Failed:" + ex.Message, ex);
exception.Log();
}
}
private XmlDocument GetSoapEnvelope(Stream stream)
{
XmlDocument xml = new XmlDocument();
try
{
stream.Position = ;
StreamReader reader = new StreamReader(stream);
xml.LoadXml(reader.ReadToEnd());
stream.Position = ;
}
catch (Exception ex)
{
ZuoraException exception = new ZuoraException(PublicEnums.ExceptionType.LogTraceFailed, "GetSoapEnvelope Failed:" + ex.Message, ex);
exception.Log();
}
return xml;
} /// <summary>
/// Copies a stream.
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
private void CopyStream(Stream from, Stream to)
{
try
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
catch (Exception ex)
{
ZuoraException exception = new ZuoraException(PublicEnums.ExceptionType.LogTraceFailed, "CopyStream Failed:" + ex.Message, ex);
exception.Log();
}
}
#endregion
}
}