Fun with SOAP Extensions

2002-9-11 11:04:50【作者】 畅享网 【进入论坛】
广告

Fun with SOAP Extensions



Keith Ballinger

Microsoft Corporation

March 22, 2001

Download or browse the VBSoapEx.exe in the MSDN Online Code Center.

Note   As promised, in this month's column we're going to look at one of the more advanced, but cooler features of ASP.NET Web Services—SOAP Extensions. For this month's column Keith Ballinger, a Program Manager for .NET Web Services, has offered to share some of his knowledge of this subject. Thanks and see you next month. -Rob
One of the more interesting things you can do with the .NET Framework Web services technology is create SOAP Extensions. These extensions allow you to gain access to the actual network stream before it is deserialized into objects within the framework, and vice versa.

SOAP Extensions allow developers to create very interesting applications on top of the core SOAP architecture found within .NET. For instance, you can implement an encryption algorithm on top of the Web Service call. Alternatively, you could implement a compression routine, or even create a SOAP Extension that will accept SOAP Attachments.

How does this work? It’s easy. First, I'd recommend that you review Rob Howard’s earlier article Web Services with ASP.NET and then come back. You may also want to read SOAP in the Microsoft .NET Framework and Visual Studio .NET.

Basically, you need to do two things:

Create a class that derives from System.Web.Services.Protocols.SoapExtension

Create a class that derives from System.Web.Services.Protocols.SoapExtensionAttribute

And you are almost done! Now, all that you have to do is create something interesting from you derived classes. Ok, so maybe that isn’t as easy as I make it sound. (Creating the interesting part is almost always the hardest part of development, isn’t it?)

For this column, we will create an extension that records incoming and outgoing SOAP messages to our Web Service. This trace extension is useful for debugging when you really care about getting the SOAP message to look exactly the way you want it to look.

The core piece of implementation you need to worry about is the ProcessMessage method. This method is called several times, and every time it sends a SoapMessage object, it includes information on the stage of the SOAP message. To illustrate, let’s examine the flow of a SOAP message within a Web Service. All of this applies on the client as well as the server, but we will concentrate on the server for this example.

A SOAP message comes in and the server figures out which method to route to.

Then the server checks to see if any SOAP extensions (that’s us!) should be invoked, and if so, invokes them with the BeforeDeserialize event stage.
Last, the server deserializes the stream and invokes all the extensions for the AfterDeserialize stage.

The key for us here is the BeforeDeserialize event stage. Here, we grab the stream and write it out to the log file:

public void WriteInput( SoapMessage message ){

MemoryStream m = new MemoryStream();

   Copy( message.Stream, m);

   m.Seek(0, SeekOrigin.Begin);

   Double now = System.DateTime.Now.ToOADate();

FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate, FileAccess.Write);

   StreamWriter w = new StreamWriter(fs);         // create a Char writer

   w.BaseStream.Seek(0, SeekOrigin.End);         // set the file pointer to the end
     
           TextReader reader = new StreamReader(m);

           String line;

           while ((line = reader.ReadLine()) != null) {

               w.WriteLine(line);

           }

           w.Flush();

           w.Close();

   m.Seek(0, SeekOrigin.Begin);

           message.Stream = m;
}

Notice we also write it out to a MemoryStream so that the SoapMessage object still has a valid stream when we are done. If we don’t do this, the server will try to deserialize the network stream that was originally on the SoapMessage, and find nothing there.

A similar process takes place when we log the SOAP message that the server will return to the client. However, there is one exception. In the BeforeSerialize stage, we need to swap out the network stream on the SoapMessage, and replace it with one of our own MemoryStreams. This slight of hand will allow us to record whatever is sent as SOAP, without immediately losing it in the network.

public void SetStream( SoapMessage message ){

        oldStream = message.Stream;

        newStream = new MemoryStream();

        message.Stream = newStream;

    }

public void WriteOutput( SoapMessage message ){

   newStream.Seek(0, SeekOrigin.Begin);

   Double now = System.DateTime.Now.ToOADate();

          FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate, FileAccess.Write);

   StreamWriter w = new StreamWriter(fs);         // create a Char writer

   w.BaseStream.Seek(0, SeekOrigin.End);         // set the file pointer to the end
      
           TextReader reader = new StreamReader(newStream);

           string line;

           while ((line = reader.ReadLine()) != null) {

               w.WriteLine(line);

           }

           w.Flush();

           w.Close();

           newStream.Seek(0, SeekOrigin.Begin);

   Copy( newStream, oldStream);

}

Notice that we record whatever was written to the memory stream, and then write it out to the network stream once we are done. If we don’t write it out to the stream we stole, the client will never get a response.

Here is the full trace extension for you to use and play with:

<%@ WebService Language="c#" Class="TestIt" %>

using System;

using System.IO;

using System.Text;

using System.Web.Services;

using System.Web.Services.Protocols;

public class TestIt{

    [WebMethod]

   [TraceExtension()]

    public Address TestMethod() {

      Address address = new Address();

      address.Street = "123 Main St.";

      address.City = "Klamath Falls";

      address.State = "Oregon";

      address.ZipCode = "97600";

        return address;
    }
}

public class Address {

   public String Street;

   public String City;

   public String State;

   public String ZipCode;
}

[AttributeUsage(AttributeTargets.Method)]

public class TraceExtensionAttribute : SoapExtensionAttribute {

    public override Type ExtensionType {

        get { return typeof(TraceExtension); }

    }
}

public class TraceExtension : SoapExtension {

    Stream oldStream;

    Stream newStream;

    public override int GetPriority() {

        return 0;

    }

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) {

        return null;

    }

    public override void Initialize(object initializer) {

    }

    public override void ProcessMessage(SoapMessage message) {

        switch (message.Stage) {

        case SoapMessageStage.BeforeSerialize:

         SetStream( message );

            break;

        case SoapMessageStage.AfterSerialize:

              WriteOutput( message );

            break;

        case SoapMessageStage.BeforeDeserialize:

         WriteInput( message );

            break;

        case SoapMessageStage.AfterDeserialize:

            break;

        default:

            throw new Exception("invalid stage");

        }

    }

    public void SetStream( SoapMessage message ){

        oldStream = message.Stream;

        newStream = new MemoryStream();

        message.Stream = newStream;

    }

   public void WriteOutput( SoapMessage message ){

      newStream.Seek(0, SeekOrigin.Begin);

      Double now = System.DateTime.Now.ToOADate();

          FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate, FileAccess.Write);

      StreamWriter w = new StreamWriter(fs);         // create a Char writer

      w.BaseStream.Seek(0, SeekOrigin.End);         // set the file pointer to the end
     
        TextReader reader = new StreamReader(newStream);

        string line;

        while ((line = reader.ReadLine()) != null) {

            w.WriteLine(line);

        }

        w.Flush();

        w.Close();

        newStream.Seek(0, SeekOrigin.Begin);

      Copy( newStream, oldStream);

    }

    public void WriteInput( SoapMessage message ){

      MemoryStream m = new MemoryStream();

      Copy( message.Stream, m);

      m.Seek(0, SeekOrigin.Begin);

      Double now = System.DateTime.Now.ToOADate();

          FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate, FileAccess.Write);

      StreamWriter w = new StreamWriter(fs);         // create a Char writer

      w.BaseStream.Seek(0, SeekOrigin.End);         // set the file pointer to the end
     
        TextReader reader = new StreamReader(m);

        string line;

        while ((line = reader.ReadLine()) != null) {

            w.WriteLine(line);

        }

        w.Flush();

        w.Close();

    m.Seek(0, SeekOrigin.Begin);

        message.Stream = m;

    }

    void Copy(Stream from, Stream to) {

        TextReader reader = new StreamReader(from);

        TextWriter writer = new StreamWriter(to);

        string line;

        while ((line = reader.ReadLine()) != null) {   
       
            writer.WriteLine(line);

        }

        writer.Flush();

    }

}

So there you have it—a trace extension. SOAP Extensions are a very useful feature, and I hope to see a lot imaginative uses for them over the next few years. There are some slight, but fundamental changes to the way extensions work in the upcoming beta 2 of the .NET Framework, but don’t worry, we'll update this code (which is for beta 1) with beta 2 code when it comes out.

Sample binaries are available on the MSDN CDs

如果您希望与本文章的作者或其所在机构,进一步交流,请联系:畅享网 姜小姐
jill.jiang@amteam.org | 021-51096826-112 | 在线联系
老孙的IT运维管理之道[原创]用户的BSM用户的IT业务管..

从企业实际的IT运营角度来看,BSM是推动IT与业务融合,实现、改善WCNG司IT管理和治理的最佳实践之一。

吕建伟 专栏和CIO问答软件项目实施管理

现实中很少能按照正规流程来的,所以只能把流程中的各个环节拆开,个个击破,以后就可以见招拆招了。

ITIL实施:CIO时刻准备着

千军易得,一将难求,要推进ITIL实施,CIO扮演的角色不容忽视。吹响集结号,CIO出击的时刻已经来到。

节能与优化IT 企业CIO过冬良策

当前金融危机的影响还在继续漫延,很多企业都在苦寻过冬的良策,在这种情况下,节能与优化技术与产品无疑成为CIO们关注的首要对象,本次选题就是针对节能与优化IT来为CIO们提供过冬的良……