Share via


SOAP 拡張機能を使用した SOAP メッセージの変更

SOAP 機能機能を使用すると、XML Web サービスまたは XML Web サービス クライアントとの間で送受信される SOAP メッセージを変更することにより、XML Web サービスの機能を拡張できます。たとえば、既存の XML Web サービスで実行するための暗号化方式または圧縮アルゴリズムを実装できます。

SOAP 拡張機能のしくみを理解するには、まず XML Web サービスの有効期間を理解しておくとわかりやすくなります。XML Web サービスの有効期間の概要については、「XML Web サービスの有効期間の分析」を参照してください。また、クライアントから XML Web サービスへの呼び出しにかかわる主な段階を次の図に簡単に示します。

XML Web サービスの有効期間の分析

ASP.NET は、図に示す各段階において、XML Web サービスのコンピュータと XML Web サービス クライアントのコンピュータの両方で XML のシリアル化および逆シリアル化を行います。SOAP 拡張機能をインフラストラクチャに導入して、各 SOAP メッセージのシリアル化または逆シリアル化の前後でそのメッセージを調べたり、変更したりできます。たとえば、ASP.NET でクライアントの引数をシリアル化した後に、暗号化の SOAP 拡張機能を使用して SOAP メッセージの XML 部分を暗号化してから Web サーバー上で SOAP メッセージを復号化し、その後で ASP.NET で SOAP メッセージを逆シリアル化するとします。SOAP 機能機能が SOAP メッセージを調べたり変更したりする可能性があるこれらの段階は、SoapMessageStage 列挙体で定義されます。この場合、SOAP 機能機能は AfterSerialize ステージで暗号化を行い、BeforeDeserialize ステージで復号化を行っています。

一般的に、SOAP 拡張機能で SOAP メッセージの内容を変更する場合には、クライアントとサーバーの両方でその内容を変更する必要があります。つまり、SOAP 拡張機能をクライアントで実行して SOAP メッセージを暗号化する場合は、対応する SOAP 拡張機能で SOAP メッセージをサーバーで復号化する必要があります。SOAP メッセージを復号化しない場合、ASP.NET インフラストラクチャでは SOAP メッセージを逆シリアル化してオブジェクトを生成できません。当然、SOAP メッセージのログだけを記録する SOAP 拡張機能など、SOAP メッセージを変更しない SOAP 拡張機能は、クライアントまたはサーバーだけで実行できます。この場合、受信側は、SOAP 拡張機能が実行されなかった場合と同じ SOAP メッセージを受信し、ASP.NET インフラストラクチャによってその SOAP メッセージを逆シリアル化できます。また、SOAP 拡張機能が SOAP を逆シリアル化できないように変更する場合を除いて、SOAP 拡張機能をクライアントおよびサーバーの両方で実行する必要はありません。

これで、SOAP 拡張機能の動作およびその機能が実行される状況についての説明は終わりです。次に、SOAP 拡張機能の構築方法について説明します。SOAP 拡張機能を構築し、その機能を XML Web サービスで実行する基本手順は、次のとおりです。

  • クラスを SoapExtension から派生します。
  • 今後使用する SOAP メッセージを表す Stream への参照を格納します。
  • SOAP 拡張機能に固有のデータを初期化します。
  • 適切な SoapMessageStage またはステージで SOAP メッセージを処理します。
  • SOAP 拡張機能を構成し、特定の XML Web サービス メソッドを使用して実行します。

SoapExtension からのクラスの派生

SoapExtension から派生するクラスは、SOAP 拡張機能の機能を実行するクラスです。つまり、SOAP 拡張機能が暗号化 SOAP 拡張機能である場合、SoapExtension クラスから派生するクラスは、暗号化とそれに対応する復号化を実行します。

今後使用する SOAP メッセージを表すストリームへの参照の格納

SOAP メッセージを変更するには、ChainStream メソッドを上書きする必要があります。これは、今後使用する SOAP メッセージ内容を取得するために使用できるストリームへの参照を受信できる唯一の機会です。

SoapMessageStage の前に、Stream への参照を ChainStream に 1 回渡します。この Stream は、優先順位の低い SOAP 拡張機能が実行されたことによって変更された SOAP メッセージの XML を参照します。SOAP 拡張機能の優先順位の詳細については、「XML Web サービス メソッドで実行する SOAP 拡張機能の構成」を参照してください。したがって、SOAP 拡張機能はこの参照をメンバ変数に格納し、後から SOAP 拡張機能が SoapMessageStage で SOAP メッセージを調べたり変更したりするときにアクセスできるようにしておく必要があります。

ただし、ChainStream に渡される Stream は、SOAP 拡張機能が変更する Stream ではありません。SOAP 機拡張能は Stream の新しいインスタンスを作成し、そのインスタンスをプライベート メンバ変数に格納して ChainStream メソッドに返します。各 SoapMessageStage で SOAP 拡張機能を実行して SOAP メッセージを変更するとき、SOAP 拡張機能は ChainStream に渡した Stream から読み取りを行い、ChainStreamStream に戻り値を書き込みます。したがって、2 つの Stream 参照を ChainStream に格納することが重要です。

ChainStream メソッドの共通の実装を次のコード例に示します。

    ' Save the Stream representing the SOAP request or SOAP response
    ' into a local memory buffer.
    Public Overrides Function ChainStream(stream As Stream) As Stream
        ' Save the passed in Stream in a member variable.
        oldStream = stream

        ' Create a new instance of a Stream and save that in a member
        ' variable.
        newStream = New MemoryStream()
        Return newStream
    End Function
[C#]
    // Save the Stream representing the SOAP request or SOAP response into
    // a local memory buffer.
    public override Stream ChainStream( Stream stream ){
        // Save the passed in Stream in a member variable.
        oldStream = stream;

        // Create a new instance of a Stream and save that in a member
        // variable.
        newStream = new MemoryStream();
        return newStream;
    }

SOAP 拡張機能の固有データの初期化

SOAP 拡張機能の内部データは、その機能が適用される XML Web サービスまたは XML Web サービス メソッドに基づいて初期化できます。たとえば、XML Web サービス メソッドとの間で送受信される SOAP メッセージのログを記録する SOAP 拡張機能で、ログ情報を格納するファイルの名前を、SOAP 拡張機能を実行する XML Web サービスまたは XML Web サービス メソッドの名前に基づいて初期化する場合があります。

SoapExtension から派生するクラスには、データを初期化するためのメソッドとして GetInitializerInitialize の 2 つがあります。GetInitializer は、SOAP 拡張機能の構成方法に応じて、その SOAP 拡張機能を実行する XML Web サービスまたは XML Web サービス メソッドに初めてアクセスした場合にだけ呼び出されます。「XML Web サービス メソッドで実行する SOAP 拡張機能の構成」を参照してください。属性を使用して SOAP 拡張機能を構成する場合は、ASP.NET インフラストラクチャによって XML Web サービス メソッドごとに GetInitializer が呼び出されます。構成ファイルで SOAP 拡張機能を構成する場合、初めて XML Web サービスにアクセスしたときにだけ、ASP.NET インフラストラクチャによって GetInitializer が呼び出されます。SOAP 拡張機能によって GetInitializer から返されたデータは、SOAP 拡張機能が後から使用できるように、ASP.NET インフラストラクチャによってキャッシュされます。Initialize メソッドで XML Web サービスまたは XML Web サービス メソッドによって SOAP 拡張機能を実行するたびに、キャッシュされたデータがその SOAP 拡張機能に渡されます。

GetInitializer メソッドで SOAP 拡張機能に有効な情報は、その SOAP 拡張機能の構成方法に応じて異なります。属性を使用して SOAP 拡張機能を構成している場合、その属性、関連するすべてのカスタム プロパティ、および LogicalMethodInfo が ASP.NET インフラストラクチャによって GetInitializer に渡されます。LogicalMethodInfo により、パラメータ数とそのデータ型など、XML Web サービス メソッドに関するプロトタイプ詳細が提供されます。構成ファイルを使用して SOAP 拡張機能を構成している場合、XML Web サービスを実装するクラスの Type だけが GetInitializer に渡されます。

SOAP 拡張機能を実際に構成している方法に基づいて、キャッシュされたデータを GetInitializer メソッド内で異なる方法で初期化するコード例を次に示します。属性を使用して SOAP 拡張機能を構成している場合、属性で指定されたファイル名 (この場合は TraceExtensionAttribute) がキャッシュされます。構成ファイルを使用して SOAP 拡張機能を構成している場合、XML Web サービスの型に基づいて、キャッシュされたファイル名が計算されます。

' When the SOAP extension is accessed for the first time, the XML
' Web service method it is applied to is accessed to store the file
' name passed in, using the corresponding SoapExtensionAttribute.
Public Overloads Overrides Function GetInitializer(methodInfo As _
   LogicalMethodInfo, attribute As SoapExtensionAttribute) As Object 
 Return CType(attribute, TraceExtensionAttribute).Filename
End Function

' The extension was configured to run using a configuration file 
' instead of an attribute applied to a specific XML Web service method.
' Return a file name, based on the class implementing the XML Web 
' service's type.

Public Overloads Overrides Function GetInitializer(WebServiceType As _
   Type) As Object
  ' Return a file name to log the trace information, based on the type.
  Return "C:\" + WebServiceType.FullName + ".log"    
End Function
[C#]
// When the SOAP extension is accessed for the first time, the XML
// Web service method it is applied to is accessed to store the file
// name passed in, using the corresponding SoapExtensionAttribute.
public override object GetInitializer(LogicalMethodInfo methodInfo,
   SoapExtensionAttribute attribute) 
{
   return ((TraceExtensionAttribute) attribute).Filename;
}
// The extension was configured to run using a configuration file instead of
// an attribute applied to a specific XML Web service method.
public override object GetInitializer(Type WebServiceType) 
{
// Return a file name to log the trace information, based on the type.
   return "C:\\" + WebServiceType.FullName + ".log";}

SOAP メッセージの処理

SoapExtension から派生したクラスでは、実装の中核部分は SoapExtension.ProcessMessage メソッドとなります。このメソッドは、SoapMessageStage 列挙体に定義されている各ステージで ASP.NET によって数回呼び出されます。SoapExtension.ProcessMessage メソッドが呼び出されるたびに、そこから派生したクラスである SoapMessage が、その特定のステージで SOAP メッセージに関する情報と共に渡されます。XML Web サービスで SOAP 拡張機能を実行する場合、SoapServerMessage が渡されます。XML Web サービス クライアントで SOAP 拡張機能を実行する場合、SoapClientMessage が渡されます。

XML Web サービスの呼び出しをトレースする SOAP 拡張機能の ProcessStage メソッドのコード例を次に示します。トレース時に、SoapMessageStage でパラメータをシリアル化して XML を生成する場合、結果の XML がファイルに書き込まれます。

    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");
        }
    }
[Visual Basic]
    Public Overrides Sub ProcessMessage(message As SoapMessage)
        Select Case message.Stage
           Case SoapMessageStage.BeforeSerialize
Case SoapMessageStage.AfterSerialize                ' Write the SOAP message out to a file.
                WriteOutput(message)Case SoapMessageStage.BeforeDeserialize' Write the SOAP messae out to a file.
                WriteInput(message)
           Case SoapMessageStage.AfterDeserialize
           Case Else
                Throw New Exception("invalid stage")
        End Select
    End Sub

SOAP 拡張機能のメソッドを呼び出す順序

SOAP 拡張機能で上書きする必要のあるメソッドの説明を終了し、次に XML Web サービス メソッドの呼び出し中に ASP.NET で SOAP 拡張機能のメソッドを呼び出すタイミングについて説明します。SOAP 拡張機能をクライアントとサーバーの両方で実行することを前提とした手順を次に示します。SOAP 拡張機能をクライアントとサーバーの両方で実行しない場合、それぞれで SOAP 拡張機能を実行するにための手順は、ASP.NET によって無視されます。

クライアント側

  1. クライアントがプロキシ クラスに対してメソッドを呼び出します。
  2. SOAP 拡張機能の新しいインスタンスがクライアントに作成されます。
  3. SOAP 拡張機能をクライアントの XML Web サービスで初めて実行する場合、クライアントで実行する SOAP 拡張機能に対して GetInitializer メソッドが呼び出されます。
  4. Initialize メソッドが呼び出されます。
  5. ChainStream メソッドが呼び出されます。
  6. SoapMessageStageBeforeSerialize に設定された ProcessMessage メソッドが呼び出されます。
  7. クライアント コンピュータ上の ASP.NET が、XML Web サービス メソッドの引数をシリアル化して XML を生成します。
  8. SoapMessageStageAfterSerialize に設定された ProcessMessage メソッドが呼び出されます。
  9. クライアント コンピュータ上の ASP.NET が、SOAP メッセージをネットワーク経由で XML Web サービスを管理する Web サーバーに送信します。

サーバー側

  1. Web サーバーの ASP.NET が SOAP メッセージを受信します。
  2. SOAP 拡張機能の新しいインスタンスが Web サーバーに作成されます。
  3. Web サーバーでは、SOAP 拡張機能をサーバー側の XML Web サービスで初めて実行する場合、サーバーで実行する SOAP 拡張機能に対して GetInitializer メソッドが呼び出されます。
  4. Initialize メソッドが呼び出されます。
  5. ChainStream メソッドが呼び出されます。
  6. SoapMessageStage が BeforeDeserialize に設定された ProcessMessage メソッドが呼び出されます。
  7. ASP.NET が XML 内の引数を逆シリアル化します。
  8. SoapMessageStageAfterDeserialize に設定された ProcessMessage メソッドが呼び出されます。
  9. ASP.NET が XML Web サービスを実装するクラスの新しいインスタンスを作成し、逆シリアル化された引数を渡して、XML Web サービス メソッドを呼び出します。このオブジェクトは、Web サーバーと同じコンピュータに存在します。
  10. XML Web サービス メソッドは、そのコードを実行し、最終的に戻り値と out パラメータを設定します。
  11. SoapMessageStage が BeforeSerialize に設定された ProcessMessage メソッドが呼び出されます。
  12. Web サーバー上の ASP.NET が戻り値と Out パラメータをシリアル化して XML を生成します。
  13. SoapMessageStageAfterSerialize に設定された ProcessMessage メソッドが呼び出されます。
  14. ASP.NET が SOAP 応答メッセージをネットワーク経由で XML Web サービス クライアントに返信します。

クライアント側

  1. クライアント コンピュータ上の ASP.NET が SOAP メッセージを受信します。
  2. SoapMessageStage が BeforeDeserialize に設定された ProcessMessage メソッドが呼び出されます。
  3. ASP.NET が XML を逆シリアル化して戻り値と Out パラメータを生成します。
  4. SoapMessageStageAfterDeserialize に設定された ProcessMessage メソッドが呼び出されます。
  5. ASP.NET が戻り値と Out パラメータをプロキシ クラスのインスタンスに渡します。
  6. クライアントが戻り値と out パラメータを受け取ります。

XML Web サービス メソッドで実行する SOAP 拡張機能の構成

SOAP 拡張機能は、カスタム属性を使用して実行するように構成することも、構成ファイルを変更することで実行するように構成することもできます。カスタム属性を使用するには、SOAP 拡張機能を実行する各 XML Web サービスメソッドにカスタム属性を適用します。構成ファイルを使用する場合は、構成ファイルのスコープ内のすべての XML Web サービスで SOAP 拡張機能が実行されます。構成ファイルのしくみの詳細については、「アプリケーションの設定」を参照してください。

カスタム属性を使用するには、SoapExtensionAttribute からクラスを派生させます。SoapExtensionAttribute には、ExtensionTypePriority の 2 つのプロパティがあります。SOAP 拡張機能は、その SOAP 拡張機能の型を ExtensionType プロパティで返す必要があります。Priority プロパティは、簡単に説明すると、SOAP 拡張機能の相対的な優先順位を表します。

構成ファイルのスコープ内のすべての XML Web サービスで SOAP 拡張機能を実行するには、適切な App.config ファイルまたは Web.config ファイルにエントリを追加します。特に、soapExtensionTypes XML 要素を構成ファイルの webServices セクションに追加する必要があります。soapExtensionTypes XML 要素内で、構成ファイルのスコープ内の各 XML Web サービスで実行するそれぞれの SOAP 拡張機能に対して add XML 要素を追加します。add XML 要素には、次のプロパティがあります。

プロパティ 説明
type SOAP 拡張機能の型およびその型が含まれるアセンブリ。
priority SOAP 拡張機能のグループ内での相対的な優先順位を指定します。
group SOAP 拡張機能がメンバとなるグループを指定します。優先順位については、次に示す詳細を参照してください。

複数の SOAP 拡張機能を 1 つの XML Web サービス メソッドで実行するように構成した場合、それらの SOAP 拡張機能には、実行順序を示す相対的な優先順位が割り当てられています。優先順位が高い SOAP 拡張機能ほど、ネットワーク経由で送受信される SOAP メッセージに対して早く実行されます。SOAP 拡張機能は、3 つの優先順位グループのいずれかのメンバになります。各グループでは、priority プロパティによって各メンバが区別されます。priority プロパティの値が小さいほど、相対的な優先順位が高くなります (0 が最も高い)。

SOAP 拡張機能の 3 つの相対的な優先順位グループは、属性を使用して構成された SOAP 拡張機能、および group 設定値が 0 または 1 である構成ファイルで指定された SOAP 拡張機能です。属性を使用して構成された SOAP 拡張機能は、中間グループのメンバになります。構成ファイルを使用して構成された SOAP 拡張機能の group 設定値が 0 の場合は、相対的な優先順位が最も高くなります。group 設定値が 1 の場合は、相対的な優先順位は最も低くなります。

相対的な優先順位グループが 0、優先順位が 1 の範囲内で Logger.LoggerExtension SOAP 拡張機能を実行することを指定する構成ファイルのコード例を次に示します。

<configuration>
 <system.web>
   <webServices>
     <soapExtensionTypes>      <add type="Logger.LoggerExtension,logger"           priority="1"           group="0" />     </soapExtensionTypes>
    </webServices>
 </system.web>
</configuration>

XML Web サービスまたは XML Web サービス クライアントとの間で送受信される SOAP メッセージのログを記録する SOAP 拡張機能のコード例を次に示します。次の SOAP 拡張機能が XML Web サービスと共に実行されるようにインストールされている場合、ASPNET ユーザー アカウントにはログ ファイルが書き込まれるディレクトリへの書き込みアクセス許可が必要です。

Imports System
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.IO

' Define a SOAP Extension that traces the SOAP request and SOAP response
' for the XML Web service method the SOAP extension is applied to.
Public Class TraceExtension
    Inherits SoapExtension
    
    Private oldStream As Stream
    Private newStream As Stream
    Private m_filename As String    
    
    ' Save the Stream representing the SOAP request or SOAP response into
    ' a local memory buffer.
    Public Overrides Function ChainStream(stream As Stream) As Stream
        oldStream = stream
        newStream = New MemoryStream()
        Return newStream
    End Function

    ' When the SOAP extension is accessed for the first time, the XML Web
    ' service method it is applied to is accessed to store the file
    ' name passed in, using the corresponding SoapExtensionAttribute.
    Public Overloads Overrides Function GetInitializer(methodInfo As _ 
        LogicalMethodInfo, _
      attribute As SoapExtensionAttribute) As Object 
      Return CType(attribute, TraceExtensionAttribute).Filename
    End Function

    ' The SOAP extension was configured to run using a configuration file
    ' instead of an attribute applied to a specific XML Web service
    ' method.  Return a file name based on the class implementing the Web
    ' Service's type.
    Public Overloads Overrides Function GetInitializer(WebServiceType As _
      Type) As Object
      ' Return a file name to log the trace information to, based on the
      ' type.
      Return "C:\" + WebServiceType.FullName + ".log"    
    End Function

    ' Receive the file name stored by GetInitializer and store it in a
    ' member variable for this specific instance.
    Public Overrides Sub Initialize(initializer As Object)
        m_filename= CStr(initializer)
    End Sub
    
    ' If the SoapMessageStage is such that the SoapRequest or SoapResponse
    ' is still in the SOAP format to be sent or received over the network,
    ' save it out to file.
    Public Overrides Sub ProcessMessage(message As SoapMessage)
        Select Case message.Stage
            Case SoapMessageStage.BeforeSerialize
            Case SoapMessageStage.AfterSerialize
                WriteOutput(message)
            Case SoapMessageStage.BeforeDeserialize
                WriteInput(message)
            Case SoapMessageStage.AfterDeserialize
            Case Else
                Throw New Exception("invalid stage")
        End Select
    End Sub
   
    ' Write the SOAP message out to a file.
    Public Sub WriteOutput(message As SoapMessage)
        newStream.Position = 0
        Dim fs As New FileStream(m_filename, FileMode.Append, _
                                 FileAccess.Write)
        Dim w As New StreamWriter(fs)
        w.WriteLine("-----Response at " + DateTime.Now.ToString())
        w.Flush()
        Copy(newStream, fs)
        w.Close()
        newStream.Position = 0
        Copy(newStream, oldStream)
    End Sub    
    
    ' Write the SOAP message out to a file.
    Public Sub WriteInput(message As SoapMessage)
        Copy(oldStream, newStream)
        Dim fs As New FileStream(m_filename, FileMode.Append, _
                                 FileAccess.Write)
        Dim w As New StreamWriter(fs)

        w.WriteLine("----- Request at " + DateTime.Now.ToString())
        w.Flush()
        newStream.Position = 0
        Copy(newStream, fs)
        w.Close()
        newStream.Position = 0
    End Sub    
    
    Sub Copy(fromStream As Stream, toStream As Stream)        
        Dim reader As New StreamReader(fromStream)
        Dim writer As New StreamWriter(toStream)
        writer.WriteLine(reader.ReadToEnd())
        writer.Flush()
    End Sub
End Class

' Create a SoapExtensionAttribute for our SOAP Extension that can be
' applied to an XML Web service method.
<AttributeUsage(AttributeTargets.Method)> _
Public Class TraceExtensionAttribute
    Inherits SoapExtensionAttribute
    
    Private m_filename As String = "c:\log.txt"
    Private m_priority As Integer    
    
    Public Overrides ReadOnly Property ExtensionType() As Type
        Get
            Return GetType(TraceExtension)
        End Get
    End Property 
    
    Public Overrides Property Priority() As Integer
        Get
            Return m_priority
        End Get
        Set
            m_priority = value
        End Set
    End Property 
    
    Public Property Filename() As String
        Get
            Return m_filename
        End Get
        Set
            m_filename= value
        End Set
    End Property
End Class
[C#]
  using System;
  using System.Web.Services;
  using System.Web.Services.Protocols;
  using System.IO;
  using System.Net;

  // Define a SOAP Extension that traces the SOAP request and SOAP
  // response for the XML Web service method the SOAP extension is
  // applied to.

  public class TraceExtension : SoapExtension 
  {
    Stream oldStream;
    Stream newStream;
    string filename;

    // Save the Stream representing the SOAP request or SOAP response into
    // a local memory buffer.
    public override Stream ChainStream( Stream stream ){
        oldStream = stream;
        newStream = new MemoryStream();
        return newStream;
    }

    // When the SOAP extension is accessed for the first time, the XML Web
    // service method it is applied to is accessed to store the file
    // name passed in, using the corresponding SoapExtensionAttribute.   
    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) 
    {
        return ((TraceExtensionAttribute) attribute).Filename;
    }

    // The SOAP extension was configured to run using a configuration file
    // instead of an attribute applied to a specific XML Web service
    // method.
    public override object GetInitializer(Type WebServiceType) 
    {
      // Return a file name to log the trace information to, based on the
      // type.
      return "C:\\" + WebServiceType.FullName + ".log";    
    }

    // Receive the file name stored by GetInitializer and store it in a
    // member variable for this specific instance.
    public override void Initialize(object initializer) 
    {
        filename = (string) initializer;
    }

    //  If the SoapMessageStage is such that the SoapRequest or
    //  SoapResponse is still in the SOAP format to be sent or received,
    //  save it out to a 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");
        }
    }

    public void WriteOutput(SoapMessage message){
        newStream.Position = 0;
        FileStream fs = new FileStream(filename, FileMode.Append,
                                       FileAccess.Write);
        StreamWriter w = new StreamWriter(fs);

      string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest";
        w.WriteLine("-----" + soapString + " at " + DateTime.Now);
        w.Flush();
        Copy(newStream, fs);
        w.Close();
        newStream.Position = 0;
        Copy(newStream, oldStream);
    }

    public void WriteInput(SoapMessage message){
        Copy(oldStream, newStream);
        FileStream fs = new FileStream(filename, FileMode.Append,
                                       FileAccess.Write);
        StreamWriter w = new StreamWriter(fs);

        string soapString = (message is SoapServerMessage) ?
                            "SoapRequest" : "SoapResponse";
        w.WriteLine("-----" + soapString + 
                    " at " + DateTime.Now);
        w.Flush();
        newStream.Position = 0;
        Copy(newStream, fs);
        w.Close();
        newStream.Position = 0;
    }

    void Copy(Stream from, Stream to) 
    {
        TextReader reader = new StreamReader(from);
        TextWriter writer = new StreamWriter(to);
        writer.WriteLine(reader.ReadToEnd());
        writer.Flush();
    }
  }

   // Create a SoapExtensionAttribute for the SOAP Extension that can be
   // applied to an XML Web service method.
  [AttributeUsage(AttributeTargets.Method)]
  public class TraceExtensionAttribute : SoapExtensionAttribute {

    private string filename = "c:\\log.txt";
    private int priority;

    public override Type ExtensionType {
        get { return typeof(TraceExtension); }
    }

    public override int Priority {
        get { return priority; }
        set { priority = value; }
    }

    public string Filename {
        get {
            return filename;
        }
        set {
            filename = value;
        }
    }
  }

参照

SoapExtension クラス | SoapExtensionAttribute クラス | SoapMessageStage 列挙体 | LogicalMethodInfo クラス | XML Web サービスの有効期間の分析 | アプリケーションの設定 | ASP.NET を使用した XML Web サービスの作成 | XML Web サービス クライアントの作成