[WCF] Usar ClientMessageInspector para añadir información a las soapHeaders

Hay ocasiones que queremos pasar información del cliente al servidor, o vicerversa y no queremos cambiar la fachada de nuestros métodos, o bien porque implicaría mucho cambio, o bien porque la información que queremos transportar es una información de “estado” que no tiene sentido que esté en la fachada de nuestros métodos.

Pues esto lo vamos a poder hacer a través de los behavior de los endpoint y de los MessageInspector, que existen de dos clases, los de cliente (IClientMessageInspector) y los de servidor (IDispatchMessageInspector). Y me vais a perdonar porque no tienen que estar necesariamente en cliente o en servidor, pero este es un punto de partida.

 

1.- Definimos una clase que va a ser nuestro MessageInspector y hacemos que implemente la interfaz IClientMessageInspector

   1: namespace ClientInspector

   2: {

   3:     public class ClientMessageInspector : System.ServiceModel.Dispatcher.IClientMessageInspector

   4:     {

   5:         #region IClientMessageInspector Members

   6:  

   7:         public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)

   8:         {

   9:  

  10:         }

  11:  

  12:         public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)

  13:         {

  14:             return Guid.NewGuid();

  15:         }

  16:  

  17:         #endregion

  18:     }

  19: }

2.- En el método BeforeSendRequest, es dónde tenemos que añadir información en las soapHeaders, en el ejemplo vamos a insertar un guid, pero se podría insertar cualquier cosa

   1: public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)

   2: {

   4:     MessageHeader<Guid> mhg = new MessageHeader<Guid>(Guid.NewGuid());

   5:     MessageHeader untyped = mhg.GetUntypedHeader("GuidToken", "gtk");            

   6:     request.Headers.Add(untyped);

   7:     return Guid.NewGuid();

   8: }

 

Ahora mismo tenemos definido el MessageInspector de cliente y lo que hará cuando se llame, lo siguiente es aplicarlo a través de un EndPointBehavior.

3.- Definimos un custom Behavior para que aplique nuestro message inspector, así que creamos una clase que implemente la interfaz IEndpointBehavior

   1: namespace ClientInspector

   2: {    

   3:     public class InspectorBehavior : IEndpointBehavior

   4:     {

   5:         #region IEndpointBehavior Members

   6:         public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

   7:         {

   8:         }

   9:  

  10:         public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)

  11:         {

  12:         }

  13:             

  14:         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)

  15:         {

  16:         }

  17:  

  18:         public void Validate(ServiceEndpoint endpoint)

  19:         {

  20:         }

  21:         #endregion        

  22:     }

  23: }

4.- En el método ApplyClientBehavior, es dónde añadimos el MessageInspector que hemos creado en los pasos anteriores

   1: public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)

   2: {

   3:     ClientMessageInspector inspector = new ClientMessageInspector();

   4:     clientRuntime.MessageInspectors.Add(inspector);

   5: }

 

Con esto ya tendríamos el behavior listo y para aplicarlo por código, en una service reference ya creada en nuestro proyecto, tendríamos que añadirlo a la lista de behaviors del EndPoint, un ejemplo de esto sería.

   1: client.Endpoint.Behaviors.Add(new ClientInspector.InspectorBehavior());

 

Con estas tres líneas se puede leer la cabecera que hemos insertado. Esto se haría en la parte “servidora”

   1: //Leer información de las cabeceras

   2: //Recoger la cabecera que ha insertado el behavior

   3: if (OperationContext.Current.IncomingMessageHeaders.FindHeader("GuidToken", "gtk") >= 0)

   4: {

   5:     Console.WriteLine(OperationContext.Current.IncomingMessageHeaders.GetHeader<Guid>("GuidToken", "gtk").ToString());

   6: }

 

Os dejo el código fuente del ejemplito aquí.

 

Ya estaría, aunque faltaría un punto bastante importante, nosotros queremos hacer todo esto a través de un fichero de configuración porque no apetece nada tener que ir por el código añadiendo el behavior a cada service reference que tengamos… ¿verdad?…

 

¿Cómo añadir un behaviorExtension al fichero de configuración?

Bueno pues ese problema también está resuelto heredando de BehaviorExtensionElement.

1.- Añadimos la clase al ejemplito anterior

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Text;

   5: using System.ServiceModel.Configuration;

   6:  

   7: namespace ClientInspector

   8: {

   9:   public class InspectorBehaviorExtension : BehaviorExtensionElement

  10:   {

  11:     /// <summary>

  12:     /// BehaviorType

  13:     /// </summary>

  14:     public override Type BehaviorType

  15:     {

  16:       get { return typeof(InspectorBehavior); }

  17:     }

  18:  

  19:     /// <summary>

  20:     /// CreateBehavior

  21:     /// </summary>

  22:     /// <returns></returns>

  23:     protected override object CreateBehavior()

  24:     {

  25:       return new InspectorBehavior();

  26:     }

  27:   }

  28: }

Con esta clase ya compilada y funcionando, ya podríamos configurar el behavior en el fichero de configuración, aplicarlo al endpoint y quitar la llamada del código que hemos puesto.

Os muestro el fichero de configuración y luego os comento las entradas “especiales” (que no tienen nada de especiales) para que esto funcione. He quitado la parte del binding… porque en este caso no nos preocupa…

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="BehaviorConInspector">
<InspectorExtension />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="InspectorExtension" type="ClientInspector.InspectorBehaviorExtension, ClientInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<bindings>
<ws2007HttpBinding>
<binding name="WS2007HttpBinding_ISampleService" ...>
</binding>
</ws2007HttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8090/SampleService" behaviorConfiguration="BehaviorConInspector"
binding="ws2007HttpBinding" bindingConfiguration="WS2007HttpBinding_ISampleService"
contract="ServiceReference1.ISampleService" name="WS2007HttpBinding_ISampleService" />
</client>
</system.serviceModel>
</configuration>

Aquí las cosas que hemos cambiado son, extensions, behaviors y el endpoint de client.

BehaviorExtension

Lo primero de todo es añadir una behaviorextension que es lo que hemos conseguido haciendo la clase que heredaba de BehaviorExtensionElement (su propio nombre lo indica), eso se añade con

<extensions>
<behaviorExtensions>
<add name="InspectorExtension" type="ClientInspector.InspectorBehaviorExtension, ClientInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>

 

Behavior

Hay que añadir un nuevo behavior que tenga nuestra extension para que se ejecute el inspector, como queremos hacer

<behaviors>
<endpointBehaviors>
<behavior name="BehaviorConInspector">
<InspectorExtension />
</behavior>
</endpointBehaviors>
</behaviors>

Aquí además se puede añadir cualquier tipo de configuración asociada a los behavior como se hace normalmente.

 

Client endpoint

Lo último, y bastante importante, es decirle a nuestro endpoint de cliente que utilice ese behavior, porque si no, esto no habría valido para nada 🙂

Para esto, sólo hay que añadir en la configuración de cliente el atributo behaviorConfiguration = “BehaviorConInspector”.

 

Y listo, ya podríamos probar quitando la línea que añade el inspector explícitamente en el código y funcionaría exactamente igual

 

Este post se acabó….

Os dejo el código fuente con esto cambiado aquí, por si queréis comparar.

Cómo habréis podido observar, me he roto los cuernos pensando los nombrecicos así que no lo toméis muy a mal… :). Esto de meter información en las cabeceras de los mensajes mola!!!. En el proyecto actual, lo usamos para quitar una sesión de servidor (sí…es malvada y no permite escalar las aplicaciones… por eso la queríamos quitar), y la verdad es que va muy bien.

 

Saludicos y hasta la próxima.

Un comentario sobre “[WCF] Usar ClientMessageInspector para añadir información a las soapHeaders”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *