Ändern der SOAP-Meldung mit SOAP-Erweiterungen

Mit SOAP-Erweiterungen können Entwickler die Funktionalität eines XML-Webdienstes erweitern, indem die von einem XML-Webdienst oder einem XML-Webdienstclient empfangenen und gesendeten SOAP-Meldungen geändert werden. Beispielsweise können Sie einen Verschlüsselungs- und Komprimierungsalgorithmus implementieren, der mit einem vorhandenen XML-Webdienst ausgeführt wird.

Um die Funktionsweise einer SOAP-Erweiterung zu verstehen, sollten Sie sich zuerst vergegenwärtigen, was unter den Phasen eines XML-Webdienstes zu verstehen ist. Eine Übersicht über die Phasen eines XML-Webdienstes erhalten Sie unter Übersicht über die Phasen eines XML-Webdienstes. In der folgenden Grafik sind die Hauptphasen eines Aufrufs dargestellt, der von einem Client an einen XML-Webdienst gesendet wird.

Übersicht der Phasen eines XML-Webdienstes

Wie Sie sehen, wird XML sowohl auf dem Computer mit dem XML-Webdienst als auch auf dem Computer mit dem XML-Webdienstclient von ASP.NET in Phasen serialisiert und deserialisiert. Eine SOAP-Erweiterung kann in die Infrastruktur eingefügt werden, um die SOAP-Meldungen vor und nach den einzelnen Serialisierungs- und Deserialisierungsphasen zu überprüfen oder zu ändern. Mit einer SOAP-Erweiterung für die Verschlüsselung kann der XML-Bereich der SOAP-Meldung beispielsweise verschlüsselt werden, nachdem die Clientargumente von ASP.NET serialisiert wurden. Anschließend kann die SOAP-Meldung auf dem Webserver entschlüsselt werden, bevor sie von ASP.NET deserialisiert wird. Diese Phasen, in denen die SOAP-Meldung durch eine SOAP-Erweiterung geprüft oder geändert werden kann, sind in der SoapMessageStage-Enumeration definiert. In diesem Fall führt die SOAP-Erweiterung die Verschlüsselung auf der AfterSerialize-Stufe und die Entschlüsselung auf der BeforeDeserialize-Stufe durch.

Wenn der Inhalt einer SOAP-Meldung von einer SOAP-Erweiterung geändert wird, müssen die Änderungen normalerweise sowohl auf dem Client als auch auf dem Server durchgeführt werden. Wenn eine SOAP-Erweiterung also auf dem Client ausgeführt werden und die SOAP-Meldung verschlüsseln soll, muss die SOAP-Meldung auf dem Server von einer entsprechenden SOAP-Erweiterung entschlüsselt werden. Wenn die SOAP-Meldung nicht entschlüsselt wird, kann die SOAP-Meldung von der ASP.NET-Infrastruktur nicht in ein Objekt deserialisiert werden. Eine SOAP-Erweiterung, durch die die SOAP-Meldung nicht geändert wird, z. B. eine einfache SOAP-Erweiterung zur Protokollierung der SOAP-Meldungen, kann selbstverständlich entweder nur auf dem Client oder nur auf dem Server ausgeführt werden. In diesem Fall erhält der Empfänger dieselbe SOAP-Meldung, die er erhalten würde, wenn die SOAP-Erweiterung nicht ausgeführt würde, und die SOAP-Meldung kann von der ASP.NET-Infrastruktur deserialisiert werden. Außerdem ist es nicht erforderlich, die SOAP-Erweiterung auf Client und Server auszuführen, solange die SOAP-Meldung von der Erweiterung nicht in der Weise geändert wird, dass eine Deserialisierung unmöglich wird.

Nachdem Sie nun wissen, was eine SOAP-Erweiterung leisten kann und wann sie ihre Funktionalität entfaltet, können Sie sich der Erstellung einer SOAP-Erweiterung zuwenden. Im Folgenden sind die grundlegenden Schritte aufgeführt, mit denen Sie eine SOAP-Erweiterung erstellen und mit einem XML-Webdienst ausführen lassen können:

  • Ableiten einer Klasse von SoapExtension.
  • Speichern eines Verweises auf den Stream, der die zukünftigen SOAP-Meldungen darstellt.
  • Initialisieren spezifischer Daten der SOAP-Erweiterung.
  • Verarbeiten der SOAP-Meldungen während der zugehörigen SoapMessageStage.
  • Konfigurieren der SOAP-Erweiterung für die Ausführung mit spezifischen XML-Webdienstmethoden.

Ableiten einer Klasse von "SoapExtension"

Die von SoapExtension abgeleitete Klasse ist die Klasse, durch die die Funktionen der SOAP-Erweiterung ausgeführt werden. Wenn eine SOAP-Erweiterung also zur Verschlüsselung dient, werden Verschlüsselung und Entschlüsselung von der Klasse ausgeführt, die von der SoapExtension-Klasse abgeleitet wurde.

Speichern eines Verweises auf den Stream, der die zukünftigen SOAP-Meldungen darstellt.

Um eine SOAP-Meldung zu ändern, müssen Sie die ChainStream-Methode überschreiben. Dies ist die einzige Möglichkeit, um einen Verweis auf den Stream zu erhalten, mit dem der Inhalt zukünftiger SOAP-Meldungen abgerufen werden kann.

Ein Verweis auf einen Stream wird vor eventuellen Vorkommen von SoapMessageStage einmal an ChainStream übergeben. Dieser Stream bezieht sich auf die XML der SOAP-Meldung, nachdem SOAP-Erweiterungen niedrigerer Priorität (Einzelheiten zu Prioritäten von SOAP-Erweiterungen finden Sie unter Konfigurieren der SOAP-Erweiterung für die Ausführung mit XML-Webdienstmethoden) ausgeführt und die erforderlichen Änderungen an der SOAP-Meldung vorgenommen wurden. Deshalb sollte dieser Verweis von einer SOAP-Erweiterung in einer Membervariablen gespeichert werden, damit später in SoapMessageStage darauf zugegriffen werden kann, wenn die SOAP-Meldung von einer SOAP-Erweiterung überprüft oder geändert wird.

Der an ChainStream übergebene Stream ist jedoch nicht der Stream, der von der SOAP-Erweiterung geändert werden sollte. Eine SOAP-Erweiterung sollte eine neue Instanz von Stream erstellen, diese in einer privaten Membervariablen speichern und innerhalb der ChainStream-Methode zurückgeben. Da die SOAP-Erweiterung immer in SoapMessageStage ausgeführt wird und dabei die SOAP-Meldung ändert, sollte eine SOAP-Erweiterung Daten aus dem an ChainStream übergebenen Stream einlesen und in den Stream-Rückgabewert für ChainStream schreiben. Deshalb ist es wichtig, beide Stream-Verweise innerhalb der ChainStream-Methode zu speichern.

Im folgenden Beispiel wird eine allgemeine Implementierung der ChainStream-Methode veranschaulicht.

    ' 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;
    }

Initialisieren spezifischer Daten der SOAP-Erweiterung

Eine SOAP-Erweiterung kann interne Daten auf der Grundlage des XML-Webdienstes oder der XML-Webdienstmethode, auf die sie angewendet wurde, initialisieren. Eine SOAP-Erweiterung zur Protokollierung der von einer XML-Webdienstmethode empfangenen und gesendeten SOAP-Meldung könnte beispielsweise den Namen einer Datei initialisieren, in der die Protokollinformationen gespeichert werden (dieser Name basiert auf dem Namen des XML-Webdienstes oder der XML-Webdienstmethode, mit denen die SOAP-Erweiterung ausgeführt wird).

Die von SoapExtension abgeleitete Klasse verfügt über zwei Methoden für die Initialisierung von Daten: GetInitializer und Initialize. GetInitializer wird nur aufgerufen, wenn erstmalig auf einen XML-Webdienst oder auf eine XML-Webdienstmethode zugegriffen wird, mit dem oder der die SOAP-Erweiterung ausgeführt werden soll. Dies hängt von der Konfiguration der SOAP-Erweiterung ab (siehe Konfigurieren der SOAP-Erweiterung für die Ausführung mit XML-Webdienstmethoden). Wenn die SOAP-Erweiterung mit einem Attribut konfiguriert wird, wird GetInitializer von der ASP.NET-Infrastruktur pro XML-Webdienstmethode aufgerufen. Wenn die SOAP-Erweiterung in einer Konfigurationsdatei konfiguriert ist, wird GetInitializer von der ASP.NET-Infrastruktur nur beim erstmaligen Zugriff auf einen XML-Webdienst aufgerufen. Die von einer SOAP-Erweiterung aus GetInitializer zurückgegebenen Daten werden von der ASP.NET-Infrastruktur zwischengespeichert, damit sie von SOAP-Erweiterungen wiederverwendet werden können. Die zwischengespeicherten Daten werden jedes Mal an die SOAP-Erweiterungen übergeben, wenn die SOAP-Erweiterung mit diesem XML-Webdienst oder dieser XML-Webdienstmethode in der Initialize-Methode ausgeführt wird.

Welche Informationen für die SOAP-Erweiterung in der GetInitializer-Methode verfügbar sind, hängt davon ab, wie die SOAP-Erweiterung konfiguriert ist. Wenn die SOAP-Erweiterung mittels eines Attributs konfiguriert wurde, wird das Attribut mit allen zugehörigen benutzerdefinierten Eigenschaften und einem LogicalMethodInfo von der ASP.NET-Infrastruktur an GetInitializer übergeben. Das LogicalMethodInfo enthält Prototypdetails über die XML-Webdienstmethode, z. B. die Anzahl der Parameter und ihre Datentypen. Wenn die SOAP-Erweiterung mittels einer Konfigurationsdatei konfiguriert wurde, wird nur der Type der Klasse, durch die der XML-Webdienst implementiert wird, an GetInitializer übergeben.

Im folgenden Codebeispiel wird der zwischengespeicherte Datenblock in der GetInitializer-Methode unterschiedlich initialisiert, je nachdem, wie die SOAP-Erweiterung letztlich konfiguriert wurde. Wenn die SOAP-Erweiterung mittels eines Attributs konfiguriert wird, z. B. TraceExtensionAttribute im folgenden Beispiel, wird der im Attribut angegebene Dateiname zwischengespeichert. Wenn die SOAP-Erweiterung mittels einer Konfigurationsdatei konfiguriert wurde, wird der zwischengespeicherte Dateiname ausgehend vom Typ des XML-Webdienstes ermittelt.

' 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";}

Verarbeiten der SOAP-Meldungen

In der von SoapExtension abgeleiteten Klasse bildet die SoapExtension.ProcessMessage-Methode das Kernstück der Implementierung. Diese Methode wird mehrere Male von ASP.NET aufgerufen, und zwar während der einzelnen, in der SoapMessageStage-Enumeration definierten Stufen. Bei jedem Aufruf der SoapExtension.ProcessMessage-Methode wird eine SoapMessage oder eine davon abgeleitete Klasse mit den für die jeweilige Stufe gültigen Informationen zur SOAP-Meldung übergeben. Wenn die SOAP-Erweiterung mit einem XML-Webdienst ausgeführt wird, wird eine SoapServerMessage übergeben. Wird sie mit einem XML-Webdienstclient ausgeführt, wird eine SoapClientMessage übergeben.

Im folgenden Codebeispiel ist die ProcessStage-Methode einer SOAP-Erweiterung dargestellt, durch die ein Aufruf eines XML-Webdienstes verfolgt wird. Wenn die Parameter während der Ablaufverfolgung in SoapMessageStage in XML serialisiert wurden, wird die XML in eine Datei geschrieben.

    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

Reihenfolge für das Aufrufen der SOAP-Erweiterungsmethoden

Nachdem Sie nun die Methoden kennen gelernt haben, die von einer SOAP-Erweiterung überschrieben werden müssen, erfahren Sie jetzt, zu welchem Zeitpunkt ASP.NET SOAP-Erweiterungsmethoden während des Aufrufs einer XML-Webdienstmethode aufruft. Bei den folgenden Schritten wird vorausgesetzt, dass die SOAP-Erweiterung sowohl auf dem Client als auch auf dem Server ausgeführt wird. Wenn die SOAP-Erweiterung nicht auf Client und Server ausgeführt wird, werden die Schritte in Zusammenhang mit der SOAP-Erweiterung, die auf Client oder Server ausgeführt werden, von ASP.NET ignoriert.

Clientseite

  1. Ein Client ruft eine Methode für die Proxyklasse auf.
  2. Eine neue Instanz der SOAP-Erweiterung wird auf dem Client erstellt.
  3. Wenn die SOAP-Erweiterung zu diesem Zeitpunkt erstmalig mit diesem XML-Webdienst auf dem Client ausgeführt wird, wird die GetInitializer-Methode für die SOAP-Erweiterung aufgerufen, die auf dem Client ausgeführt wird.
  4. Die Initialize-Methode wird aufgerufen.
  5. Die ChainStream-Methode wird aufgerufen.
  6. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf BeforeSerialize festgelegt ist.
  7. Die Argumente der XML-Webdienstmethode werden von ASP.NET auf dem Clientcomputer in XML serialisiert.
  8. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf AfterSerialize gesetzt ist.
  9. Das auf dem Clientcomputer installierte ASP.NET sendet die SOAP-Meldung über das Netzwerk an den Webserver, auf dem der XML-Webdienst gespeichert ist.

Serverseite

  1. Das auf dem Webserver installierte ASP.NET empfängt die SOAP-Meldung.
  2. Eine neue Instanz der SOAP-Erweiterung wird auf dem Webserver erstellt.
  3. Wenn die SOAP-Erweiterung zu diesem Zeitpunkt erstmalig mit diesem XML-Webdienst auf Serverseite ausgeführt wird, wird die GetInitializer-Methode auf dem Webserver für die SOAP-Erweiterung aufgerufen, die auf dem Server ausgeführt wird.
  4. Die Initialize-Methode wird aufgerufen.
  5. Die ChainStream-Methode wird aufgerufen.
  6. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf BeforeDeserialize gesetzt ist.
  7. ASP.NET deserialisiert die Argumente innerhalb der XML.
  8. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf AfterDeserialize gesetzt ist.
  9. ASP.NET erstellt eine neue Instanz der Klasse, durch die der XML-Webdienst implementiert wird, und ruft die XML-Webdienstmethode auf; dabei werden die deserialisierten Argumente übergeben. Dieses Objekt befindet sich auf demselben Computer wie der Webserver.
  10. Der Code der XML-Webdienstmethode wird ausgeführt, und abschließend werden der Rückgabewert und alle Ausgabeparameter festgelegt.
  11. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf BeforeSerialize gesetzt ist.
  12. Das auf dem Webserver installierte ASP.NET serialisiert den Rückgabewert und die Ausgabeparameter in XML.
  13. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf AfterSerialize gesetzt ist.
  14. ASP.NET sendet die SOAP-Antwortmeldung über das Netzwerk zurück an den XML-Webdienstclient.

Clientseite

  1. Das auf dem Clientcomputer installierte ASP.NET empfängt die SOAP-Meldung.
  2. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf BeforeDeserialize gesetzt ist.
  3. ASP.NET deserialisiert die XML in den Rückgabewert und in entsprechende Ausgabeparameter.
  4. Die ProcessMessage-Methode wird mit SoapMessageStage aufgerufen, die auf AfterDeserialize gesetzt ist.
  5. ASP.NET übergibt den Rückgabewert und eventuelle Ausgabeparameter an die Instanz der Proxyklasse.
  6. Der Client empfängt den Rückgabewert und eventuelle Ausgabeparameter.

Konfigurieren der SOAP-Erweiterung für die Ausführung mit XML-Webdienstmethoden

Eine SOAP-Erweiterung kann so konfiguriert werden, dass sie mit Hilfe eines benutzerdefinierten Attributs oder durch Änderung einer Konfigurationsdatei ausgeführt wird. Um ein benutzerdefiniertes Attribut zu verwenden, wenden Sie das Attribut auf jede XML-Webdienstmethode an, mit der die SOAP-Erweiterung ausgeführt werden soll. Wenn eine Konfigurationsdatei verwendet wird, wird die SOAP-Erweiterung mit allen XML-Webdiensten ausgeführt, die im Gültigkeitsbereich der Konfigurationsdatei liegen. Ausführliche Informationen zur Funktionsweise der Konfigurationsdateien finden Sie unter Konfigurieren von Anwendungen.

Um ein benutzerdefiniertes Attribut zu verwenden, leiten Sie eine Klasse von SoapExtensionAttribute ab. SoapExtensionAttribute verfügt über zwei Eigenschaften: ExtensionType und Priority. Der Typ der SOAP-Erweiterung sollte von einer SOAP-Erweiterung in der ExtensionType-Eigenschaft zurückgegeben werden. Die Priority-Eigenschaft stellt die relative Priorität der SOAP-Erweiterung dar, auf die kurz eingegangen wird.

Um festzulegen, dass eine SOAP-Erweiterung mit allen XML-Webdiensten ausgeführt wird, die innerhalb des Gültigkeitsbereichs einer Konfigurationsdatei liegen, fügen Sie der entsprechenden Datei App.config oder Web.config Einträge hinzu. Genauer ausgedrückt heißt dies, dass dem Abschnitt webServices der Konfigurationsdatei ein XML-Element soapExtensionTypes hinzugefügt werden muss. Innerhalb des XML-Elements soapExtensionTypes fügen Sie für jede SOAP-Erweiterung, die mit den einzelnen XML-Webdiensten im Gültigkeitsbereich der Konfigurationsdatei ausgeführt werden soll, XML-Elemente hinzu. Das XML-Element add verfügt über die folgenden Eigenschaften.

Eigenschaft Beschreibung
type Der Typ der SOAP-Erweiterung und die Assembly, in der sie sich befindet.
priority Die relative Priorität der SOAP-Erweiterung innerhalb ihrer Gruppe.
group Die Gruppe, der die SOAP-Erweiterung als Member angehört. Details zur Priorität finden Sie im Anschluss an diese Tabelle.

SOAP-Erweiterungen wird eine Priorität zugewiesen, durch die die relative Ausführungsreihenfolge vorgegeben wird, wenn mehrere SOAP-Erweiterungen für die Ausführung mit einer XML-Webdienstmethode konfiguriert sind. Je höher die Priorität einer SOAP-Erweiterung ist, desto näher liegt ihre Ausführung an der über das Netzwerk gesendeten oder empfangenen SOAP-Meldung. SOAP-Erweiterungen gehören einer von drei Prioritätsgruppen an. Innerhalb jeder Gruppe werden die einzelnen Member durch die priority-Eigenschaft unterschieden. Je niedriger die priority-Eigenschaft ist, desto höher ist die relative Priorität (0 entspricht der höchsten Prioritätsstufe).

Die drei relativen Prioritätsgruppen für SOAP-Erweiterungen sind: SOAP-Erweiterungen, die mit einem Attribut konfiguriert sind, und SOAP-Erweiterungen, für die in der Konfigurationsdatei eine group-Einstellung von 0 oder 1 festgelegt wurde. Mit einem Attribut konfigurierte SOAP-Erweiterungen gehören der mittleren Gruppe an. Mit einer Konfigurationsdatei konfigurierte SOAP-Erweiterungen mit einer group-Einstellung von 0 haben die höchste relative Priorität. Erweiterungen mit einer group-Einstellung von 1 haben die niedrigste relative Priorität.

Das folgende Codebeispiel enthält eine Konfigurationsdatei, durch die festgelegt wird, dass die SOAP-Erweiterung Logger.LoggerExtension mit der relativen Prioritätsgruppe 0 ausgeführt wird und die Priorität 1 hat.

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

Das folgende Codebeispiel enthält eine SOAP-Erweiterung, durch die die von einem XML-Webdienst oder einem XML-Webdienstclient empfangenen oder gesendeten SOAP-Meldungen protokolliert werden. Wenn die folgende SOAP-Erweiterung für die Ausführung mit einem XML-Webdienst installiert wurde, muss das Benutzerkonto ASPNET über die Schreibberechtigung für das Verzeichnis verfügen, in das auch die Protokolldatei geschrieben wird.

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;
        }
    }
  }

Siehe auch

SoapExtension | SoapExtensionAttribute | SoapMessageStage | LogicalMethodInfo | Übersicht über die Phasen eines XML-Webdienstes | Konfigurieren von .NET Framework-Anwendungen | Erstellen von XML-Webdiensten mit ASP.NET | Erstellen von XML-Webdienstclients