现在的位置: 首页 > 综合 > 正文

无侵入方面编程-用HttpModule+SoapExtension监视页面执行参数(二)

2011年02月15日 ⁄ 综合 ⁄ 共 9868字 ⁄ 字号 评论关闭

    上一篇文章 "无侵入方面编程-用HttpModule+SoapExtension监视页面执行参数(一)"中,我们实现了监视每个页面的执行情况和调用WebService的简单信息。

    这次我们继续深入一下SoapExtension的应用,在不改变Soap的WSDL文档的情况下,配合在Dotnet编写的WebService站点配置我们编写的SoapExtension,来穿透传输我们自定义的数据对象。由于SoapExtension是全局的,我们还要加一些标识来区分服务器是否已经配置了我们的SoapExtension,从而不影响其它的WebService调用。

    在SoapExtension中,我想到有两种方案:
    一种是直接在SoapMessage.Headers中插入自定义的SoapHeader对象,然后在客户端的序列化后,从SoapUnknownHeader中取出数据,然后反序列化成自定义的对象。
    第二种是对SoapMessage.ContentType 添加一个额外的标识,在另一方检测这个标识,来从流中取出自己长度的数据。反序列化成自定义的对象。

    我感觉第二种方案比较简单,高效,我们看一下代码:

    /// <summary>
    
/// WSInvokeMonitorExtension 的摘要说明。
    
/// </summary>
    public class StreamWSInvokeMonitorExtension : SoapExtension
    {
        
private const string WSInvokeMonitorKey = "__WSInvokeMonitorKey__";
        
private const int MaxDataLength = 5 * 1024 * 1024;  //5M
        private XmlSerializer serializer = new XmlSerializer(typeof(DbConnInfo));
        
private Stream originStream;
        
private Stream usingStream = new MemoryStream();
        
private WSInvokeInfo invokeInfo = new WSInvokeInfo();
        
public override System.IO.Stream ChainStream(System.IO.Stream stream)
        {
            originStream 
= stream;
            
return usingStream;
        }

        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)
        {
            
if(message is SoapClientMessage)
            {
                
switch (message.Stage) 
                {
                    
case SoapMessageStage.BeforeSerialize:
                        
this.invokeInfo.BeginInvokeTime = DateTime.Now;
                        
this.invokeInfo.MethodName = message.MethodInfo.Name;
                        
break;

                    case SoapMessageStage.AfterSerialize:
                        usingStream.Seek(
0, SeekOrigin.Begin);
                        CopyStream(usingStream, originStream);
                        
break;

                    case SoapMessageStage.BeforeDeserialize:
                        usingStream.SetLength(
0);
                        CopyStream(originStream, usingStream);
                        usingStream.Seek(
0, SeekOrigin.Begin);
                        
if(message.ContentType.IndexOf("ExtInfo=true"!= -1){
                            
//先读出自己的信息
                            byte[] bytesFlags = new byte[]{66,69}; // BE  标识

                            
if(usingStream.ReadByte() == bytesFlags[0])  //检验标志位
                            {
                                
byte[] bytesLength = new byte[BitConverter.GetBytes((Int32)0).Length];  
                                usingStream.Read(bytesLength,
0, bytesLength.Length); //读取长度
                                int length = BitConverter.ToInt32(bytesLength,0);

                                if(length < MaxDataLength)   //检验数据是否非法
                                {
                                    
if(usingStream.ReadByte() == bytesFlags[1])   //检验标志位
                                    {
                                        
byte[] bytesData = new byte[length];
                                        usingStream.Read(bytesData,
0, bytesData.Length);
                                        
using(MemoryStream ms = new MemoryStream(bytesData))
                                        {
                                            DbConnInfo header 
= serializer.Deserialize(ms) as DbConnInfo;
                                            Debug.WriteLine(header.Info);
                                        }

                                        break;
                                    }

                                }

                            }

                            //数据不对,则重置流位置
                            usingStream.Position = 0;
                        }

                        break;

                        // About to call methods
                    case SoapMessageStage.AfterDeserialize:
                        
//添加到Module记录
                        this.invokeInfo.EndInvokeTime = DateTime.Now;
                        PageInfo pageInfo 
= (PageInfo)HttpContext.Current.Items[WSInvokeMonitorKey] ;
                        
if(pageInfo != null)
                        {
                            pageInfo.AppendInvokeInfo(
this.invokeInfo);
                        }

                        break;

                        // After Method call

                    
default:
                        
throw new Exception("No stage such as this");
                }

            }
            else if(message is SoapServerMessage)
            {
                
switch (message.Stage) 
                {
                        
// Incoming from client
                    case SoapMessageStage.BeforeDeserialize:
                        usingStream.SetLength(
0);
                        CopyStream(originStream, usingStream);
                        usingStream.Seek(
0, SeekOrigin.Begin);

                        break;

                        // About to call methods
                    case SoapMessageStage.AfterDeserialize:
                        
break;

                        // After Method call
                    case SoapMessageStage.BeforeSerialize:
                        
                        
//设置自定义Header
                        usingStream.SetLength(0);  //清空并先写入自定义的信息
                        

                        byte[] bytesData = null;
                        
using(MemoryStream ms = new MemoryStream())
                        {
                            serializer.Serialize(ms,
new DbConnInfo("3个数据库连接"));  //服务端自定义的信息
                            bytesData 
= ms.ToArray();
                        }
                        message.ContentType 
+= ";ExtInfo=true";
                        
//写入标识
                        byte[] bytesFlags = new byte[]{66,69}; // BE
                        usingStream.WriteByte(bytesFlags[0]);

                        //写入长度
                        byte[] dataLength = BitConverter.GetBytes(bytesData.Length);
                        usingStream.Write(dataLength, 
0, dataLength.Length);

                        //再写入标志
                        usingStream.WriteByte(bytesFlags[1]);

                        //写入数据
                        usingStream.Write(bytesData, 0, bytesData.Length);
                        
                        
break;

                        // Outgoing to other 
                    case SoapMessageStage.AfterSerialize:
                        usingStream.Seek(
0, SeekOrigin.Begin);
                        CopyStream(usingStream, originStream);
                        
break;

                    default:
                        
throw new Exception("No stage such as this");
                }

            }
        }

        private void CopyStream(Stream src, Stream des)
        {
            
byte[] buf = new byte[1024];
            
int length = 0;
            
do 
            {
                length 
= src.Read(buf,0,buf.Length);
                des.Write(buf,
0,length);
            } 
while (length == buf.Length);          

        }
    }

    [System.Serializable]
    public class DbConnInfo 
    {
        
private string info;
        
public string Info
        {
            
get { return info; }
            
set { info = value; }
        }

        public DbConnInfo()
        {
        }

        public DbConnInfo(string info)
        {
            
this.Info = info;
        }
        
    }

   代码中,在服务端写入标识: message.ContentType += ";ExtInfo=true";

   然后在客户端检测:if(message.ContentType.IndexOf("ExtInfo=true"!= -1){...}
   这样便保证了不会影响其它没有加载Soap扩展的调用。

   到目前为至,我们已经成功的把服务端的信息对象,在不改变WSDL的情况下,无侵入的传递到调用端。具体收集什么信息,就要在WebService端进行收集了!我这里的要求是收集数据库连接的情况,下一次继续介绍吧。

   附:方案一实现代码:

 

方案一代码

 

 

抱歉!评论已关闭.