Compartir a través de


Alterar el mensaje SOAP mediante extensiones SOAP

Las extensiones SOAP permiten que los programadores amplíen la funcionalidad de un servicio Web XML mediante la alteración de los mensajes SOAP enviados y recibidos por un servicio o un cliente de servicios Web XML. Por ejemplo, puede implementar un algoritmo de cifrado o compresión para que se ejecute con un servicio Web XML existente.

Para entender el funcionamiento de una extensión SOAP, es útil comprender primero el período de duración de un servicio Web XML. Para obtener información general acerca de la duración de un servicio Web XML, vea Anatomía de un período de duración de servicio Web XML. En el gráfico siguiente se describen las fases principales de la llamada de un cliente a un servicio Web XML.

Anatomía de un período de duración de servicio Web XML

Como puede ver, ASP.NET serializa y deserializa el código XML durante las fases tanto en el equipo donde reside el servicio Web XML como en el equipo del cliente de servicios Web XML. Una extensión SOAP se puede insertar en la infraestructura para inspeccionar o modificar los mensajes SOAP antes y después de cada una de estas fases de serialización y deserialización. Por ejemplo, una extensión SOAP de cifrado puede cifrar la parte XML del mensaje SOAP después de que ASP.NET serialice los argumentos del cliente y, a continuación, descifrar el mensaje SOAP en el servidor Web antes de que ASP.NET deserialice el mensaje SOAP. Estas fases, en las que una extensión SOAP puede inspeccionar o modificar el mensaje SOAP, se definen en la enumeración SoapMessageStage. En este caso, la extensión SOAP se cifra en la etapa AfterSerialize y se descifra en la etapa BeforeDeserialize.

Por lo general, cuando una extensión SOAP modifica el contenido de un mensaje SOAP, las modificaciones se deben realizar en el cliente y en el servidor. Es decir, si una extensión SOAP se va a ejecutar en el cliente y va a cifrar el mensaje SOAP, en el servidor debe existir una extensión SOAP correspondiente que descifre el mensaje SOAP. Si no se descifra el mensaje SOAP, la infraestructura de ASP.NET no podrá deserializar el mensaje SOAP en un objeto. Por supuesto, una extensión SOAP que no modifique el mensaje SOAP, como una extensión que sólo registre los mensajes SOAP, se puede ejecutar en el cliente o en el servidor solamente. En este caso, el destinatario recibe el mismo mensaje SOAP que si no se ejecutara una extensión SOAP y la infraestructura de ASP.NET puede deserializar el mensaje SOAP. Además, si una extensión SOAP no modifica el mensaje de modo que no se pueda llevar a cabo la deserialización, no es necesario que la extensión SOAP se ejecute en el cliente y en el servidor al mismo tiempo.

Una vez que se conocen las funciones de una extensión SOAP y cuándo se pueden llevar a cabo, se puede examinar la forma de crearla. Los pasos básicos para crear una extensión SOAP y hacer que se ejecute con un servicio Web XML son los siguientes:

  • Derivar una clase de SoapExtension.
  • Guardar una referencia a la clase Stream que representa los futuros mensajes SOAP.
  • Inicializar los datos específicos de la extensión SOAP
  • Procesar los mensajes SOAP durante las etapas SoapMessageStage pertinentes.
  • Configurar la extensión SOAP para que se ejecute con métodos de servicio Web XML específicos.

Derivar una clase de SoapExtension

La clase que se deriva de SoapExtension es la que realiza las funciones de la extensión SOAP. Es decir, si la extensión SOAP es de cifrado, la clase que se deriva de SoapExtension realiza el cifrado y el descifrado correspondiente.

Guardar una referencia a la clase Stream que representa los futuros mensajes SOAP

Para modificar un mensaje SOAP, debe reemplazar el método ChainStream, ya que es la única oportunidad de recibir una referencia a la secuencia que se puede utilizar para obtener el contenido de los futuros mensajes SOAP.

Una referencia a una clase Stream se pasa a ChainStream una vez, antes de cualquier SoapMessageStage. Esta clase Stream hace referencia al código XML del mensaje SOAP después de que se han ejecutado las extensiones SOAP de menor prioridad (vea Configurar la extensión SOAP para que se ejecute con métodos de servicio Web XML para obtener más información acerca de las prioridades de las extensiones SOAP) y se han realizado los cambios en el mensaje SOAP. Por lo tanto, una extensión SOAP debe guardar esta referencia en una variable miembro para el acceso posterior durante SoapMessageStage, cuando inspeccione o modifique el mensaje SOAP.

Sin embargo, la Stream pasada a ChainStream no es la que una extensión SOAP debe modificar. Una extensión SOAP debe crear una nueva instancia de una clase Stream, guardarla en una variable miembro y devolverla en el método ChainStream. Como la extensión SOAP se ejecuta durante cada SoapMessageStage y modifica el mensaje SOAP, una extensión SOAP debe leer la clase Stream pasada al método ChainStream y escribir en el valor devuelto Stream del método ChainStream. Por lo tanto, es importante guardar ambas referencias a Stream en el método ChainStream.

En el ejemplo siguiente se muestra una implementación común del método 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;
    }

Inicializar los datos específicos de la extensión SOAP

Una extensión SOAP puede inicializar los datos internos en función del servicio Web XML o el método de servicio Web XML al que se aplique. Por ejemplo, una extensión SOAP que registre el mensaje SOAP enviado y recibido por un método de servicio Web XML puede inicializar el nombre de un archivo para guardar la información de registro (basado en el nombre del servicio Web XML o el método de servicio Web XML con el que se ejecute la extensión SOAP).

La clase que se deriva de SoapExtension tiene dos métodos para inicializar los datos: GetInitializer e Initialize. GetInitializer sólo se llama la primera vez que se tiene acceso al servicio o método de servicio Web XML con el que la extensión SOAP está configurada para ejecutarse, dependiendo de cómo esté configurada (vea Configurar la extensión SOAP para que se ejecute con métodos de servicio Web XML). Si la extensión SOAP está configurada con un atributo, la infraestructura de ASP.NET llama a GetInitializer para cada método de servicio Web XML. Si está configurada en un archivo de configuración, la infraestructura de ASP.NET sólo llama a GetInitializer la primera vez que se tiene acceso a un servicio Web XML. La infraestructura de ASP.NET almacena en caché los datos devueltos en GetInitializer por una extensión SOAP para uso futuro de otras extensiones SOAP. Los datos de la caché se pasan a las extensiones SOAP cada vez que éstas se ejecutan con dicho servicio Web XML o método de servicio Web XML en el método Initialize.

La información disponible para una extensión SOAP en el método GetInitializer depende de cómo esté configurada. Si la extensión SOAP está configurada con un atributo, la infraestructura de ASP.NET pasa a GetInitializer el atributo, incluidas todas las propiedades personalizadas asociadas con él, y una clase LogicalMethodInfo. La clase LogicalMethodInfo proporciona detalles de prototipo acerca del método de servicio Web XML, como el número de parámetros y sus tipos de datos. Si la extensión SOAP se configura mediante un archivo de configuración, sólo se pasa a GetInitializer el valor Type de la clase que implementa el servicio Web XML.

En el ejemplo de código siguiente se inicializa el dato almacenado en caché del método GetInitializer de forma distinta, en función de cómo se configure finalmente la extensión SOAP. Si se configura con un atributo, en este caso TraceExtensionAttribute, se almacena en caché el nombre de archivo especificado en el atributo. Si se configura mediante un archivo de configuración, se calcula el nombre de archivo de la caché en función del tipo del servicio 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";}

Procesar los mensajes SOAP

En la clase derivada de SoapExtension, la parte principal de la implementación es el método SoapExtension.ProcessMessage. ASP.NET llama a este método varias veces en cada etapa definida en la enumeración SoapMessageStage. Cada vez que se llama al métodoSoapExtension.ProcessMessage, se pasa una clase SoapMessage o una clase que se derive de ésta, con información acerca del mensaje SOAP en esa etapa determinada. Si la extensión SOAP se ejecuta con un servicio Web XML, a continuación se pasa una clase SoapServerMessage. Si la extensión SOAP se ejecuta en un cliente de servicios Web XML, entonces se pasa una clase SoapClientMessage.

El ejemplo de código siguiente contiene el método ProcessStage de una extensión SOAP que hace un seguimiento de una llamada a un servicio Web XML. Durante el seguimiento, si en SoapMessageStage se han serializado los parámetros en XML, el código XML se escribe en un archivo.

    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

Ordenar que se invoquen los métodos de extensiones SOAP

Una vez vistos los métodos que una extensión SOAP debe reemplazar, a continuación se examinará cuándo ASP.NET llama a métodos de extensiones SOAP a través de la invocación de un método de servicio Web XML. En los pasos siguientes se supone que la extensión SOAP se ejecuta en el cliente y en el servidor. Si no se ejecuta en ambos, ASP.NET pasará por alto los pasos asociados con la ejecución de la extensión SOAP en cada uno.

En el cliente

  1. Un cliente llama a un método en la clase de proxy.
  2. Una nueva instancia de la extensión SOAP se crea en el cliente.
  3. Si ésta es la primera vez que se ejecuta esta extensión SOAP en este servicio Web XML en el cliente, se invoca el método GetInitializer en la extensión SOAP que se ejecuta en el cliente.
  4. Se invoca el método Initialize.
  5. Se invoca el método ChainStream.
  6. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a BeforeSerialize.
  7. ASP.NET en el equipo cliente serializa en XML los argumentos del método de servicio Web XML.
  8. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a AfterSerialize.
  9. En el equipo cliente, ASP.NET envía el mensaje SOAP a través de la red al servidor Web que aloja el servicio Web XML.

En el servidor

  1. En el servidor Web, ASP.NET recibe el mensaje SOAP.
  2. Una nueva instancia de la extensión SOAP se crea en el servidor Web.
  3. Si ésta es la primera vez que se ejecuta esta extensión SOAP en este servicio Web XML en el servidor, se invoca el método GetInitializer en la extensión SOAP que se ejecuta en el servidor.
  4. Se invoca el método Initialize.
  5. Se invoca el método ChainStream.
  6. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a BeforeDeserialize.
  7. ASP.NET deserializa los argumentos en el código XML.
  8. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a AfterDeserialize.
  9. ASP.NET crea una nueva instancia de la clase que implementa el servicio Web XML, llama al método de servicio Web XML y le pasa los argumentos deserializados. Este objeto reside en el mismo equipo que el servidor Web.
  10. El método de servicio Web XML ejecuta el código y establece finalmente el valor devuelto y los parámetros out.
  11. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a BeforeSerialize.
  12. En el servidor Web, ASP.NET serializa en XML el valor devuelto y los parámetros out.
  13. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a AfterSerialize.
  14. ASP.NET envía el mensaje de respuesta SOAP a través de la red de vuelta al cliente de servicios Web XML.

En el cliente

  1. En el equipo cliente, ASP.NET recibe el mensaje SOAP.
  2. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a BeforeDeserialize.
  3. ASP.NET deserializa el código XML en el valor devuelto y los parámetros out.
  4. Se invoca el método ProcessMessage con la enumeración SoapMessageStage establecida a AfterDeserialize.
  5. ASP.NET pasa el valor devuelto y los parámetros out a la instancia de la clase de proxy.
  6. El cliente recibe el valor devuelto y los parámetros out.

Configurar la extensión SOAP para que se ejecute con métodos de servicio Web XML

La ejecución de una extensión SOAP se puede configurar mediante un atributo personalizado o mediante la modificación de un archivo de configuración. Para utilizar un atributo personalizado, aplique éste a cada método de servicio Web XML con el que desee que se ejecute la extensión SOAP. Si se utiliza un archivo de configuración, la extensión SOAP se ejecuta con todos los servicios Web basados en XML dentro del ámbito del archivo de configuración. Para obtener más información acerca del funcionamiento de los archivos de configuración, vea Configurar aplicaciones.

Para utilizar un atributo personalizado, derive una clase de SoapExtensionAttribute. La clase SoapExtensionAttribute tiene dos propiedades: ExtensionType y Priority. Una extensión SOAP debe devolver el tipo de la extensión SOAP en la propiedad ExtensionType. La propiedad Priority representa la prioridad relativa de la extensión SOAP, que se describe brevemente.

Para especificar que una extensión SOAP se ejecute con todos los servicios Web XML dentro del ámbito de un archivo de configuración, agregue entradas en el archivo App.config o Web.config correspondiente. En particular, se debe agregar un elemento XML soapExtensionTypes a la sección webServices del archivo de configuración. En el elemento XML soapExtensionTypes, agregue elementos XML para cada extensión SOAP que desee ejecutar con cada servicio Web XML dentro del ámbito del archivo de configuración. El elemento XML Add tiene las propiedades siguientes:

Propiedad Descripción
type Tipo de la extensión SOAP y el ensamblado en que reside.
priority Prioridad relativa de la extensión SOAP en su grupo.
group Grupo del que una extensión SOAP es miembro. Vea la información proporcionada en esta tabla sobre la prioridad.

Las extensiones SOAP tienen asignada una prioridad que impone el orden relativo de ejecución cuando hay varias extensiones SOAP configuradas para ejecutarse con un método de servicio Web XML. Cuanto mayor sea la prioridad de una extensión SOAP, más próxima se ejecutará del mensaje SOAP que se va a enviar o recibir a través de la red. Las extensiones SOAP pueden pertenecer a uno de tres grupos de prioridad. En cada grupo, la propiedad priority distingue cada miembro. Cuanto menor sea la propiedad priority, mayor será la prioridad relativa (0 es el valor más alto).

Los tres grupos de prioridad relativa para las extensiones SOAP son: extensiones SOAP configuradas mediante un atributo y extensiones SOAP especificadas en el archivo de configuración con el valor 0 o 1 para la opción group. Las extensiones SOAP configuradas mediante un atributo son miembros del grupo intermedio. Las que están configuradas mediante un archivo de configuración con el valor 0 en la opción group tienen la prioridad relativa más alta. Las que tienen el valor 1 en la opción group tienen la prioridad relativa más baja.

En el siguiente archivo de configuración de ejemplo se especifica que la extensión SOAP Logger.LoggerExtension se ejecuta en el grupo de prioridad relativa 0 y tiene una prioridad de 1.

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

El ejemplo de código siguiente es una extensión SOAP que registra los mensajes SOAP enviados y recibidos por un servicio Web XML o un cliente de servicios Web XML. Si se instala la siguiente extensión SOAP para ejecutar un servicio Web XML, la cuenta de usuario de ASPNET debe tener permiso de escritura en el directorio en que se escribe el archivo de registro.

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

Vea también

SoapExtension | SoapExtensionAttribute | SoapMessageStage | LogicalMethodInfo | Anatomía de un período de duración de servicio Web XML | Configurar aplicaciones | Generar servicios Web XML mediante ASP.NET | Generar clientes de servicios Web XML