Modifica del messaggio SOAP tramite le estensioni SOAP

Le estensioni SOAP consentono agli sviluppatori di incrementare le funzionalità di un servizio Web XML modificando i messaggi SOAP inviati a e da un servizio Web XML o un client del servizio Web XML. È possibile implementare, ad esempio, un algoritmo di crittografia o di compressione da eseguire con un servizio Web XML esistente.

Per comprendere il funzionamento di un'estensione SOAP, è importante analizzare la durata di un servizio Web XML. Per informazioni generali sulla durata di un servizio Web XML, vedere Analisi della durata di un servizio Web XML. Nel grafico seguente vengono illustrate le fasi principali di una chiamata da un client a un servizio Web XML.

Analisi della durata di un servizio Web XML

In ASP.NET, XML viene serializzato e deserializzato durante le fasi sia sul computer del servizio Web XML che sul computer client del servizio Web XML. È possibile inserire un'estensione SOAP nell'infrastruttura per analizzare o modificare i messaggi SOAP prima e dopo ogni fase di serializzazione o deserializzazione. È ad esempio possibile utilizzare un'estensione SOAP di crittografia per crittografare la porzione XML del messaggio SOAP dopo la serializzazione degli argomenti del client in ASP.NET, quindi decrittografare il messaggio SOAP sul server Web prima che il messaggio SOAP venga deserializzato in ASP.NET. Queste fasi, in cui è possibile utilizzare un'estensione SOAP per analizzare o modificare il messaggio SOAP, vengono definite nell'enumerazione SoapMessageStage. In questo caso l'estensione SOAP viene utilizzata per eseguire la crittografia nella fase AfterSerialize e la crittografia nella fase BeforeDeserialize.

In genere, quando un'estensione SOAP modifica i contenuti di un messaggio SOAP, le modifiche devono essere apportate sia sul client che sul server. Pertanto, se sul client viene eseguita un'estensione SOAP che effettua la crittografia del messaggio SOAP, sarà necessario utilizzare un'estensione SOAP corrispondente per decrittografare il messaggio SOAP sul server. Se il messaggio SOAP non viene decrittografato, l'infrastruttura ASP.NET non può deserializzare il messaggio SOAP in un oggetto. Un'estensione SOAP che non modifica il messaggio SOAP, quale un'estensione SOAP che registra semplicemente i messaggi SOAP, può essere eseguita solo sul client o sul server. In questo caso il destinatario riceve lo stesso messaggio SOAP che riceverebbe se un'estensione SOAP non fosse in esecuzione e l'infrastruttura ASP.NET può deserializzare il messaggio SOAP. Inoltre, se l'estensione SOAP non modifica il SOAP in modo da rendere la deserializzazione impossibile, non è necessario che l'estensione SOAP venga eseguita sia sul client che sul server.

Dopo aver illustrato le funzionalità di un'estensione SOAP e le situazioni in cui sono eseguite, vengono descritte le modalità di generazione di un'estensione SOAP. Di seguito sono riportati i passaggi di base per generare un'estensione SOAP ed eseguirla con un servizio Web XML.

  • Derivare una classe da SoapExtension.
  • Salvare un riferimento nel flusso,Stream, che rappresenta i futuri messaggi SOAP.
  • Inizializzare i dati specifici dell'estensione SOAP.
  • Elaborare i messaggi SOAP durante la fase SoapMessageStage o le fasi appropriate.
  • Configurare l'estensione SOAP in modo che venga eseguita con metodi di servizio Web XML specifici.

Derivazione di una classe da SoapExtension

La classe che deriva da SoapExtension rappresenta la classe che esegue le funzionalità dell'estensione SOAP. Pertanto, se l'estensione SOAP è un'estensione SOAP di crittografia, la classe che deriva dalla classeSoapExtension eseguirà la crittografia e la corrispondente decrittografia.

Salvataggio di un riferimento nel flusso che rappresenta i messaggi SOAP futuri

Per modificare un messaggio SOAP, è necessario eseguire l'override del metodo ChainStream, in quanto si tratta dell'unico modo per ricevere un riferimento al flusso che può essere utilizzato per ottenere il contenuto dei messaggi SOAP futuri.

Un riferimento a uno Stream viene passato in ChainStream una volta, prima di qualsiasi SoapMessageStage. Lo Stream fa riferimento all'XML del messaggio SOAP dopo che le estensioni SOAP con priorità inferiore sono state eseguite e hanno apportato le proprie modifiche al messaggio SOAP. Per informazioni dettagliate sulle priorità delle estensioni SOAP, vedere Configurazione dell'estensione SOAP per l'esecuzione con i metodi di servizio Web XML. È quindi opportuno che un'estensione SOAP salvi questo riferimento in una variabile membro per l'accesso in un momento successivo durante SoapMessageStage, quando il messaggio SOAP viene analizzato o modificato da un'estensione SOAP.

Lo Stream passato in ChainStream, tuttavia, non è lo Stream che deve essere modificato da un'estensione SOAP. Un'estensione SOAP deve creare una nuova istanza di uno Stream, salvarla in una variabile membro privata e restituirla all'interno del metodo ChainStream. Mentre l'estensione SOAP viene eseguita durante ogni SoapMessageStage e modifica il messaggio SOAP, è necessario che un'estensione SOAP legga dallo Stream passato in ChainStream e scriva nel valore restituito Stream per ChainStream. È quindi importante salvare entrambi i riferimenti Stream all'interno del metodo ChainStream.

Nell'esempio seguente viene descritta un'implementazione comune del metodo 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;
    }

Inizializzare i dati specifici dell'estensione SOAP

Un'estensione SOAP può inizializzare dati interni in base al servizio Web XML o al metodo di servizio Web XML al quale è applicata. Un'estensione SOAP che registra il messaggio SOAP inviato a e da un metodo di servizio Web XML, ad esempio, potrebbe inizializzare il nome di un file per salvare le informazioni sulla registrazione, in base al nome del servizio Web XML o al metodo di servizio Web XML con cui viene eseguita l'estensione SOAP.

La classe che deriva da SoapExtension dispone di due metodi per inizializzare i dati: GetInitializer e Initialize. GetInitializer viene chiamato solo la prima volta che si accede a un servizio Web XML o a un metodo di servizio Web XML con il quale è configurata l'esecuzione dell'estensione SOAP, in base alla configurazione dell'estensione SOAP (vedere Configurazione dell'estensione SOAP per l'esecuzione con i metodi di servizio Web XML). Se l'estensione SOAP viene configurata tramite un attributo, GetInitializer viene chiamato dall'infrastruttura ASP.NET per ogni metodo di servizio Web XML. Se l'estensione SOAP viene configurata in un file di configurazione, GetInitializer viene chiamato dall'infrastruttura ASP.NET solo la prima volta che si accede a un servizio Web XML. I dati restituiti da GetInitializer tramite un'estensione SOAP vengono memorizzati nella cache dall'infrastruttura ASP.NET per l'utilizzo futuro tramite le estensioni SOAP. Le estensioni SOAP vengono passate ai dati memorizzati nella cache ogni volta che l'estensione SOAP viene eseguita con il servizio Web XML o il metodo di servizio Web XML nel metodo Initialize.

Le informazioni disponibili per un'estensione SOAP nel metodo GetInitializer dipendono dalla configurazione dell'estensione SOAP. Se l'estensione SOAP viene configurata tramite un attributo, quest'ultimo, inclusa qualsiasi proprietà personalizzata a esso associata, e LogicalMethodInfo vengono passati in GetInitializer dall'infrastruttura ASP.NET. LogicalMethodInfo fornisce dettagli prototipo sul metodo di servizio Web XML, quali il numero di parametri e i relativi tipi di dati. Se l'estensione SOAP viene configurata utilizzando un file di configurazione, solo il Type della classe che implementa il servizio Web XML viene passato a GetInitializer.

Nell'esempio di codice seguente il blocco di dati memorizzato nella cache viene inizializzato in modo diverso nel metodo GetInitializer, in base a come verrà configurata l'estensione SOAP. Se l'estensione SOAP viene configurata utilizzando un attributo, in questo caso TraceExtensionAttribute, il nome file specificato nell'attributo viene memorizzato nella cache. Se l'estensione SOAP viene configurata tramite un file di configurazione, il nome file memorizzato nella cache viene calcolato in base al tipo di servizio Web XML.

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

Elaborazione dei messaggi SOAP

Nella classe derivata da SoapExtension, la parte fondamentale dell'implementazione è il metodo SoapExtension.ProcessMessage. Questo metodo viene chiamato numerose volte da ASP.NET a ogni fase definita nell'enumerazione SoapMessageStage. Ogni volta che il metodo SoapExtension.ProcessMessage viene chiamato, SoapMessage, o una classe che deriva da esso, viene passato con le informazioni relative al messaggio SOAP in quella fase specifica. Se l'estensione SOAP viene eseguita con un servizio Web XML, verrà passato un SoapServerMessage. Se l'estensione SOAP viene eseguita con un client di un servizio Web XML, verrà passato un SoapClientMessage.

Nell'esempio di codice seguente viene mostrato il metodo ProcessStage di un'estensione SOAP che analizza una chiamata a un servizio Web XML. Durante l'analisi, se in SoapMessageStage i parametri sono stati serializzati in XML, l'XML viene scritto su un file.

    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

Richiamo dei metodi delle estensioni SOAP degli ordini

Dopo avere illustrato i metodi di cui un'estensione SOAP deve eseguire l'override, verranno esaminate le situazioni in cui i metodi di estensione SOAP vengono richiamati in ASP.NET durante la chiamata di un metodo di servizio Web XML. Nei passaggi seguenti si presuppone che l'estensione SOAP sia in esecuzione sia sul client che sul server. Se l'estensione SOAP non è in esecuzione sia sul client che sul server, i passaggi associati all'estensione SOAP in esecuzione su entrambi vengono ignorati da ASP.NET.

Lato client

  1. Il client richiama un metodo sulla classe proxy.
  2. Viene creata una nuova istanza dell'estensione SOAP sul client.
  3. Se è la prima volta che l'estensione SOAP è stata eseguita sul client con il servizio Web XML, il metodo GetInitializer viene richiamato sull'estensione SOAP in esecuzione sul client.
  4. Viene richiamato il metodo Initialize.
  5. Viene richiamato il metodo ChainStream.
  6. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su BeforeSerialize.
  7. In ASP.NET sul computer client gli argomenti del metodo di servizio Web XML vengono serializzati in XML.
  8. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su AfterSerialize.
  9. In ASP.NET sul computer client viene inviato il messaggio SOAP sulla rete al server Web che contiene il servizio Web XML.

Lato server

  1. In ASP.NET sul server Web viene ricevuto il messaggio SOAP.
  2. Viene creata una nuova istanza dell'estensione SOAP sul server Web.
  3. Sul server Web, se è la prima volta che l'estensione SOAP è stata eseguita con il servizio Web XML sul lato server, viene richiamato il metodo GetInitializer sull'estensione SOAP in esecuzione sul server.
  4. Viene richiamato il metodo Initialize.
  5. Viene richiamato il metodo ChainStream.
  6. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su BeforeDeserialize.
  7. In ASP.NET gli argomenti vengono deserializzati all'interno dell'XML.
  8. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su AfterDeserialize.
  9. In ASP.NET viene creata una nuova istanza della classe che implementa il servizio Web XML e viene richiamato il metodo di servizio Web XML passando gli argomenti deserializzati. Questo oggetto si trova sullo stesso computer del server Web.
  10. Viene eseguito il codice del metodo del servizio Web XML, impostando alla fine il valore restituito e gli eventuali parametri out.
  11. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su BeforeSerialize.
  12. In ASP.NET sul server Web il valore restituito e i parametri out vengono serializzati in XML.
  13. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su AfterSerialize.
  14. In ASP.NET il messaggio di risposta SOAP viene restituito tramite la rete al client del servizio Web XML.

Lato client

  1. In ASP.NET sul computer client viene ricevuto il messaggio SOAP.
  2. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su BeforeDeserialize.
  3. In ASP.NET, l'XML viene deserializzato nel valore restituito e in qualsiasi parametro out.
  4. Viene richiamato il metodo ProcessMessage con SoapMessageStage impostato su AfterDeserialize.
  5. In ASP.NET il valore restituito e qualsiasi parametro out vengono passati all'istanza della classe proxy.
  6. Il client riceve il valore restituito e i parametri out.

Configurazione dell'estensione SOAP per l'esecuzione con i metodi di servizio Web XML

Un'estensione SOAP può essere configurata per essere eseguita utilizzando un attributo personalizzato o modificando un file di configurazione. Per utilizzare un attributo personalizzato, applicarlo a ogni metodo di servizio Web XML che si desidera eseguire con l'estensione SOAP. Quando viene utilizzato un file di configurazione, l'estensione SOAP viene eseguita con tutti i servizi Web XML all'interno dell'ambito del file di configurazione. Per informazioni dettagliate sul funzionamento dei file di configurazione, vedere Configurazione di applicazioni.

Per utilizzare un attributo personalizzato, derivare una classe da SoapExtensionAttribute. SoapExtensionAttribute dispone di due proprietà: ExtensionType e Priority. Un'estensione SOAP dovrebbe restituire il tipo di estensione SOAP nella proprietà ExtensionType. La proprietà Priority rappresenta la priorità relativa dell'estensione SOAP, che viene descritta brevemente.

Per specificare che un'estensione SOAP deve essere eseguita con tutti i servizi Web XML all'interno dell'ambito di un file di configurazione, aggiungere voci al file di configurazione App.config o Web.config appropriato. In particolare, è necessario aggiungere un elemento XML soapExtensionTypes alla sezione webServices del file di configurazione. All'interno dell'elemento XML soapExtensionTypes, aggiungere elementi XML per ciascuna estensione SOAP che si desidera eseguire con ogni servizio Web XML all'interno dell'ambito del file di configurazione. L'elemento XML add dispone delle seguenti proprietà.

Proprietà Descrizione
type Tipo dell'estensione SOAP e assembly in cui risiede.
priority Priorità relativa dell'estensione SOAP all'interno del gruppo.
group Gruppo di cui è membro un'estensione SOAP. Informazioni dettagliate sulla priorità sono riportate di seguito.

Le estensioni SOAP dispongono di una priorità assegnata che determina l'ordine relativo di esecuzione quando più estensioni SOAP vengono configurate per essere eseguite con un metodo di servizio Web XML. Più è alta la priorità di un'estensione SOAP, prima verrà eseguita sul messaggio SOAP inviato o ricevuto sulla rete. Le estensioni SOAP si trovano in uno dei tre gruppi di priorità. All'interno di ciascun gruppo, la proprietà priority distingue ogni membro. Più la proprietà priority è bassa, più alta è la priorità relativa (0 è il valore più alto).

I tre gruppi di priorità relative per le estensioni SOAP sono: estensioni SOAP configurate utilizzando un attributo ed estensioni SOAP specificate nel file di configurazione impostando group su 0 o 1. Le estensioni SOAP configurate tramite un attributo sono membri del gruppo intermedio. Le estensioni SOAP configurate tramite un file di configurazione impostando group su 0 presentano la priorità relativa più alta. Quelle con group impostata su 1 presentano la priorità relativa più bassa.

Nell'esempio di codice che segue viene mostrato un file di configurazione in cui è specificato che l'estensione SOAP Logger.LoggerExtension viene eseguita all'interno del gruppo di priorità relativa 0 e ha priorità 1.

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

Nell'esempio di codice seguente viene mostrata un'estensione SOAP che registra i messaggi SOAP inviati a e da un servizio Web XML o un client del servizio Web XML. Se l'estensione SOAP riportata di seguito è installata in modo che venga eseguita con un servizio Web XML, l'account utente ASP.NET dovrà disporre di autorizzazioni di scrittura per la directory in cui è memorizzato il file di log.

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

Vedere anche

SoapExtension | SoapExtensionAttribute | SoapMessageStage | LogicalMethodInfo | Analisi della durata di un servizio Web XML | Configurazione di applicazioni | Generazione di servizi Web XML mediante ASP.NET | Generazione di client dei servizi Web XML