Soap Extension调用原理分析
Soap扩展调用顺序原理分析
最近学习了一下WebService的Soap扩展开发,在学习时我发现最大的问题是不同优先级的SoapExtension的调用顺序以及chainStream方法的用途,我们知道SoapMessage具有一个Stream属性,那么为什么我们不能在SoapExtension的ProcessMessage中直接操作这个Stream呢,例如你的代码可能会是这个样子:
publicoverridevoidProcessMessage(SoapMessagemessage)
{
switch (message.Stage)
{
//序列化之后压缩这个流
caseSoapMessageStage.AfterSerialize:
message.Stream = CompressStream(message.Stream);
break;
//反序列化之前解压缩这个流
caseSoapMessageStage.BeforeDeserialize:
message.Stream = DeCompressStream(message.Stream);
break;
default:
break;
}
}
///<summary>
///压缩输入流,返回压缩后的新流。
///</summary>
///<param name="inputStream">输入流,该流将被压缩</param>
///<returns>返回压缩后的新流。</returns>
privateStreamCompressStream(StreaminputStream)
{
//simulate code here
returnnewMemoryStream();
}
///<summary>
///解压缩输入流,返回解压后的新流。
///</summary>
///<param name="compressedStream">被压缩的输入流</param>
///<returns>返回解压后的新流。</returns>
privateStreamDeCompressStream(StreamcompressedStream)
{
//simulate code here
returnnewMemoryStream();
}
我非常希望这段代码能够正确执行,但是在WebService调用时,在第一个CompressStream就无法通过,因为其为空。看了下面我的解释可能您能找到答案。
继续之前请首先记住如下几个结论:
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扩展优先级从高到低执行的。
从下面这段代码我们就可以看出ProcessMessage调用顺序:
internalvoidRunExtensions(SoapExtension[] extensions, bool throwOnException)
{
// extensions已经按照优先级由高到底排好序
if (extensions != null)
{
//在BeforeDeSerialize或者AfterDeSerialize阶段按照由高到低的优先级顺序执行
if ((this.stage & (SoapMessageStage.AfterDeserialize | SoapMessageStage.BeforeDeserialize)) != ((SoapMessageStage) 0))
{
for (int i = 0; i < extensions.Length; i++)
{
extensions[i].ProcessMessage(this);
if ((this.Exception != null) && throwOnException)
{
throw this.Exception;
}
}
}
else
{
//在BeforeSerialize或者AfterSerialize阶段按照由低到高的优先级顺序执行
for (int j = extensions.Length - 1; j >= 0; j--)
{
extensions[j].ProcessMessage(this);
if ((this.Exception != null) && throwOnException)
{
throw this.Exception;
}
}
}
}
}
4、SoapExtension的优先级越高,其越接近网络,即其越接近网络传输层数据。
5、WebService调用过程基本可以分为两个阶段:1、从客户端发出请求到服务器端接受请求的过程刚好就是ProcessMessage执行的四个阶段,2、从服务器端发出响应到客户端接受响应其也是执行ProcessMessage的四个阶段状态,其执行chainStream和SoapExtension执行顺序和1完全一样,只不过在第一步中传输的是SoapClientMessage,而第二步中传输的是SoapServerMessage。
6、我们知道SoapExtension的Priority属性为0,其优先级越高,如果我们现在有两个SoapExtension,分别是CompressExtension(用于压缩和解压缩)和CryptoExtension(用于加密和解密),我们现在希望其执行顺序是这样的,在客户端发出请求时,首先压缩然后加密(这样可能获得较高性能)。我们知道在客户端发出请求时ProcessMessage的执行是先从低优先级执行的,所以我们将CryptoExtension的优先级设置为高于CompressExtension的优先级即可。
我们来看一下客户端通过WebService代理发出请求的具体执行流程。
1. 客户端创建代理实例,进行必要初始化,如设置SoapHeader;
2. 客户端通过代理实例调用Web方法;
3. 代理实例调用基类SoapHttpClientProtocol的Invoke方法,将Web方法作为字符串传递进去;
4. SoapHttpClientProtocol 根据Url获取WebRequest;
5. SoapHttpClientProtocol 根据本地代理配置文件或者特性利用反射生成本地 SoapReflectedExtension对象,并且对其进行排序,该对象即SoapExtension的反射形式类,其主要作用是动态生成SoapExtension对象,并且调用其GetInitializer(第一次调用)和Initialize方法,主要是为了满足性能需求;
6. 创建SoapClientMessage实例,并且设置其Stream和SoapExtensionStream为一个空的SoapExtensionStream的实例;
7. 按照优先级顺序调用SoapExtension的ChainStream方法;并且将第六步的SoapExtensionStream传递进去;并将ChainStream的返回值设置为SoapMessage的Stream属性。这样最低优先级的SoapExtension的ChainStream返回值和SoapMessage的Message属性为同一个对象;
8. 设置SoapMessage的Stage为BeforeSerialize,按照由低到高的优先级顺序调用SoapExtension的ProcessMessage方法;
9. 设置Soap标头信息;
10. 获取WebRequest的RequestStream对象,如果当前SoapExtensionStream不为空则将该Stream绑定给SoapExtensionStream的InnerStream对象,这样就使得最高优先级的SoapExtension可以引用该Stream.否则直接将SoapMessage的Stream设置为RequestStream。
11. 序列化Soap调用的参数,并且将其写入SoapMessage的Stream属性中。
12. 设置SoapMessage的Stage为AfterSerialize,按照由低到高的优先级顺序调用SoapExtension的ProcessMessage方法;这里需要特别留意:由于在第7步中的最低优先级SoapExtension的Stream已经被第11步写入数据,所以在该步中第一个调用的SoapExtension的_newStream(_workStream)就具有了原始SOAP的消息流。
这就是我们在扩展SoapExtension时为啥在调用AfterSerialize之后_newStream中具有数据的原因。
13. 在SoapExtension扩展时的BeforeDeserialize阶段,由于ChainStream和ProcessMessage都是按照由高到低的顺序执行的,所以我们可以知道最接近网络的SoapExtension首先传入的是一个网络流(NetworkStream),所以我们在ChainStream中保存的_oldStream就是该网络流。
14. 在服务器端其执行步骤和客户端执行步骤基本完全一样,只不过在传输过程中是SoapServerMessage。
这是一个MSDN的实例,请注意其中的注释语句。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.IO;
usingSystem.Web;
usingSystem.Web.Services;
usingSystem.Web.Services.Protocols;
namespaceSoapExtensionLib
{
///<summary>
///跟踪soap请求和响应。
///</summary>
publicclassTraceExtension:SoapExtension
{
privateStream_newStream;//可以传给低扩展的流
privateStream_oldStream;//优先级高的扩展传递进来的流
privatestring_fileName;//日志文件路径
publicoverrideStreamChainStream(Streamstream)
{
_oldStream = stream;
_newStream = newMemoryStream();
return_newStream;
}
publicoverridevoidProcessMessage(SoapMessagemessage)
{
switch (message.Stage)
{
caseSoapMessageStage.BeforeSerialize:
break;
caseSoapMessageStage.AfterSerialize:
WriteOutput(message);
break;
caseSoapMessageStage.BeforeDeserialize:
WriteInput(message);
break;
caseSoapMessageStage.AfterDeserialize:
break;
}
}
privatevoidWriteOutput(SoapMessagemessage)
{
//这里是准备向网络传输的,即客户端发出请求和服务器端发出响应时发生的。
//在这两个阶段ChainStream调用顺序和ProcessMessage相反,所以可以肯定_newStream
//一定不为空,而_oldStream为空。
_newStream.Position = 0;
FileStreamfs = newFileStream(_fileName, FileMode.Append, FileAccess.Write);
StreamWriterwriter = newStreamWriter(fs);
writer.WriteLine("date time:" + DateTime.Now.ToString());
writer.Flush();
CopyStream(_newStream, fs);
writer.Close();
//这里非常重要,因为需要将低优先级低的扩展逐级向上复制给高优先级的扩展
_newStream.Position = 0;
CopyStream(_newStream, _oldStream);
}
privatevoidWriteInput(SoapMessagemessage)
{
//这里是从网络传输进来的,即服务器端接受请求和客户端接受响应时发生的。
//在这两个阶段ChainStream调用顺序和ProcessMessage相同,所以可以肯定_oldStream
//一定不为空,而_newStream为空。
CopyStream(_oldStream, _newStream);
FileStreamfs = newFileStream(_fileName, FileMode.Append, FileAccess.Write);
StreamWriterwriter = newStreamWriter(fs);
writer.WriteLine("date time:" + DateTime.Now.ToString());
writer.Flush();
_newStream.Position = 0;
CopyStream(_newStream, fs);
writer.Close();
_newStream.Position = 0;
}
privatevoidCopyStream(Streamfrom, Streamto)
{
TextReaderreader = newStreamReader(from);
TextWriterwriter = newStreamWriter(to);
writer.WriteLine(reader.ReadToEnd());
writer.Flush();
}
publicoverrideobjectGetInitializer(TypeserviceType)
{
return"c:""" + serviceType.FullName + ".log";
}
publicoverrideobjectGetInitializer(LogicalMethodInfomethodInfo, SoapExtensionAttributeattribute)
{
return ((TraceExtensionAttribute)attribute).FileName;
}
publicoverridevoidInitialize(objectinitializer)
{
_fileName = (string)initializer;
}
}
}
我们以一个压缩SOAP消息的扩展为例结束本专题。
首先定义一个SoapExtension的扩展类CompressExtension
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.IO;
usingSystem.Web;
usingSystem.Web.Services;
usingSystem.Web.Services.Protocols;
usingICSharpCode.SharpZipLib.Core;
usingICSharpCode.SharpZipLib.Zip;
namespaceSoapExtensionLib
{
///<summary>
///提供压缩和解压缩的Soap扩展。
///</summary>
publicclassCompressExtension:SoapExtension
{
privateStream_workStream;
privateStream_oldStream;
privateint_compressLevel;
privatestaticreadonlyint_DEFAULTLEVEL=9;
publicoverrideStreamChainStream(Streamstream)
{
_oldStream = stream;
_workStream = newMemoryStream();
return_workStream;
}
publicoverrideobjectGetInitializer(TypeserviceType)
{
return_DEFAULTLEVEL;
}
publicoverrideobjectGetInitializer(LogicalMethodInfomethodInfo, SoapExtensionAttributeattribute)
{
return ((CompressExtensionAttribute)attribute).CompressLevel;
}
publicoverridevoidInitialize(objectinitializer)
{
_compressLevel = (int)initializer;
}
publicoverridevoidProcessMessage(SoapMessagemessage)
{
switch (message.Stage)
{
caseSoapMessageStage.AfterSerialize:
CompressStream();
break;
caseSoapMessageStage.BeforeDeserialize:
DeCompressStream();
break;
default:
break;
}
}
privatevoidCompressStream()
{
//利用zip算法将_workStream压缩到_oldStream中,
//_oldStream将传递给高优先级的SoapExtension进行处理。
_workStream.Position = 0;
ZipOutputStreamzipStream = newZipOutputStream(_oldStream);
zipStream.SetLevel(_compressLevel);
byte[] buffer = newbyte[4096];
ZipEntryentry = newZipEntry("compress");
zipStream.PutNextEntry(entry);
StreamUtils.Copy(_workStream, zipStream, buffer);
zipStream.Finish();
}
privatevoidDeCompressStream()
{
//利用zip算法将_oldStream解压缩到_workStream中。
//_workStream将传输给低优先级的SoapExtension进行处理。
ZipInputStreamzipStream = newZipInputStream(_oldStream);
ZipEntrytheEntry = null;
byte[] buffer = newbyte[4096];
while ((theEntry = zipStream.GetNextEntry()) != null)
{
intreadByt = 0;
do{
readByt = zipStream.Read(buffer, 0, buffer.Length);
_workStream.Write(buffer, 0, readByt);
}
while (readByt > 0);
}
_workStream.Position = 0;
}
}
}
再定义一个特性:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.IO;
usingSystem.Web;
usingSystem.Web.Services;
usingSystem.Web.Services.Protocols;
namespaceSoapExtensionLib
{
[AttributeUsage(AttributeTargets.Method)]
publicclassCompressExtensionAttribute:SoapExtensionAttribute
{
privateint_priority;
privateint_level;
publicCompressExtensionAttribute()
{
_level = 9;
_priority = 0;
}
publicoverrideTypeExtensionType
{
get { returntypeof(CompressExtension); }
}
publicintCompressLevel
{
get { return_level; }
set { _level = value; }
}
publicoverrideintPriority
{
get
{
return_priority;
}
set
{
_priority = value;
}
}
}
}
下面是这个测试的WebService
usingSystem;
usingSystem.Web;
usingSystem.Web.Services;
usingSystem.Web.Services.Protocols;
usingSoapExtensionLib;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
publicclassService : System.Web.Services.WebService
{
publicService () {
//如果使用设计的组件,请取消注释以下行
//InitializeComponent();
}
[WebMethod]
[SoapExtensionLib.CompressExtension(CompressLevel = 9)]
publicstringHelloWorld() {
return"Hello World";
}
[WebMethod]
[SoapExtensionLib.CompressExtension(CompressLevel=9)]
publicstringCaculateString()
{
return"Zhang xuebao";
}
}