giovedì 22 novembre 2012

venerdì 16 novembre 2012

Loggare Request e Response nei WebServices WCF

Avete mai avuto la necessità di loggare la Request e la Response del vostro WebServiceClient fatto con WCF?
Io purtroppo, si, ed ho scoperto con mio sommo disappunto che non esiste una maniera semplice, tipo un evento a cui potersi agganciare.
Ad un certo punto ho anche perso la speranza che si potesse fare, e che si dovesse capire cosa passa solo frapponendo un proxy nel mezzo.
Voi mi direte, ma non hai il wsdl da cui creare il proxy, perché devi capire cosa passa? Lavorate per mezza giornata con le pubbliche amministrazioni e vi accorgerete che i wsdl sono una cosa abbastanza aleatoria...
Comunque, pensando solo agli aspetti meramente tecnici della questione, vediamo come si può fare a sniffare Request e Response HTML
Il trucco sta tutto nella classe IEndpointBehavior. In pratica esiste un meccanismo (molto sofisticato, devo dire) per cui ad un WebServiceClient si possono agganciare dei comportamenti custom a runtime.
Nel nostro caso ci interessa gestire i comportamenti lato client, quindi andremo ad implementare il metodo ApplyClientBehavior


namespace Utility.Behavior
{
  public class ClientTraceBehavior : IEndpointBehavior
  {
    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
      //nop
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
      clientRuntime.MessageInspectors.Add(new ClientTraceExtension());
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
      //nop
    }

    public void Validate(ServiceEndpoint endpoint)
    {
      //nop
    }
  }
}
Come si vede nell'esempio abbiamo aggiunto ai MessageInspectors del client un oggetto ClientTraceExtension, che implementa l'interfaccia IClientMessageInspector
Questo oggetto contiene gli eventi BeforeSendRequest e AfterReceiveReply che, come dicono i nomi,  vengono chiamati prima di inviare la Request e dopo aver ricevuto la Response.

namespace Utility.Behavior
{

    /// 
    /// SOAP Extension that traces the SOAP request and SOAP response
    /// 
    /// 
    public class ClientTraceExtension : IClientMessageInspector
    {
        private static string xmlRequest;
        private static string xmlResponse;

        public static string XmlRequest {
            get { return xmlRequest; }
        }

        public static string XmlResponse
        {
            get { return xmlResponse; }
        }

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            xmlResponse = reply.ToString();
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            xmlRequest = request.ToString();
            return null;
        }
    }
}

Manca solo un passo perché tutto funzioni: far scattare il meccanismo. Basta aggiungere il behaviour all'instanza del nostro client.


  ClientWs client = new ClientWs();
  client.Endpoint.Behaviors.Add(new ClientTraceBehavior());//adds the behavior

Quindi, se vorremo loggare , ci basterà farlo subito dopo aver chiamato il motodo del client, visto che ClientTraceExtension.XmlRequest e ClientTraceExtension.XmlResponse sono statici.


  client.callWebMethod();
  System.Console.WriteLn("Request: " + ClientTraceExtension.XmlRequest);
  System.Console.WriteLn("Response: " + ClientTraceExtension.XmlResponse);  

Et Voilà!
Devo dire che il meccanismo, se pur complesso, è molto ben fatto, permettendo di customizzare il WebService fino a stravolgerlo totalmente.

Credits: Per risolvere il problema sono partito da questo articolo.