Tuesday, February 24, 2009

Inspecting Messages with an IClientMessageInspector

I was building up a library which consumes a RESTful API, and needed some way of intercepting the incoming messages before serialisation so I could inject some custom logic. The API is facebooks one in particular which isn't really truly RESTful but that's another story.

Effectively some of the service methods can either return the expected return message if nothing has gone wrong, or an exception POX message if something has gone wrong. This could range from a mal-formed querystring exception to permissions or an incorrect method call. So I wanted some way to catch this and throw a proper .NET exception instead of WCF throwing a could not deserialise exception when it's unable to hydrate an object designed to match this message:

<?xml version="1.0" encoding="UTF-8"?>
<fql_query_response 
  xmlns="http//api.facebook.com/1.0/" 
  xmlnsxsi="http//www.w3.org/2001/XMLSchema-instance" 
  xsischemaLocation="http//api.facebook.com/1.0/ http//api.facebook.com/1.0/facebook.xsd"
  list="true">
  <user>
    <name>Mark Zuckerberg</name>
  </user>
  <user>
    <name>Chris Hughes</name>
  </user>
</fql_query_response>


with this message:

<?xml version="1.0" encoding="UTF-8"?>
<error_response
  xmlns="http://api.facebook.com/1.0/"
  xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation ="http://api.facebook.com/1.0/ http://api.facebook.com/1.0/facebook.xsd">
  <error_code>5</error_code>
  <error_msg>Unauthorized source IP address (ip was 10.1.2.3)</error_msg>
</error_response>


So I really wanted to hook into the point at which WCF does the deserialising, catch the exception, and then rethrow my custom facebook exception. However I wasn't able to pull that off (lack of documentation) so for now I'm simply inspecting each message individually. This is not the best solution but will do until I figure out how to do it more gracefully and efficiently. It serves as an example for now.

The first thing we need to do is add a new behaviour to our Endpoint which will subsequently add our new message inspection logic.


var customServiceProxy = new ChannelFactory<ICustomService>("CustomService");
customServiceProxy.Endpoint.Behaviors.Add(new HookServiceBehaviour());


The next step is to add our new message inspector.


public class HookServiceBehaviour  IEndpointBehavior     
{         
    #region Implementation of IEndpointBehavior               
    
    public void Validate(ServiceEndpoint endpoint){}         
   
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters){}         
    
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, 
        EndpointDispatcher endpointDispatcher){}                  
    
    public void ApplyClientBehavior(ServiceEndpoint endpoint,                
        ClientRuntime clientRuntime)        
    {            
        clientRuntime.MessageInspectors.Add(new CustomMessageInspector());        
    }     
        
    #endregion  
}


And implement our custom message inspector:


public class CustomMessageInspector  IClientMessageInspector    
{         
    #region Implementation of IClientMessageInspector              
    public object BeforeSendRequest(ref Message request,             
        IClientChannel channel)         
    {             
        return request;         
    }              
    public void AfterReceiveReply(ref Message reply, object correlationState)        
    {            
        MessageBuffer messageBuffer = reply.CreateBufferedCopy(int.MaxValue);            
        Message message = messageBuffer.CreateMessage();                 
        XmlDictionaryReader contents = message.GetReaderAtBodyContents();                        
        
        //inspect contents and do what you want, throw a custom              
        //exception for example...                 
        //We need to recreate the reply to resume the deserialisation as it             
        //can only be read once.            
        reply = messageBuffer.CreateMessage();            
        messageBuffer.Close();        
    }             
    #endregion    
}


An actual implementation of this example can be found here: http://www.codeplex.com/fluentfacebook

1 comment:

  1. Pennsylvania, Virginia and West Virginia are all of the states that border MD, with on-line sports betting being authorized in all of those, with a number of|numerous|a selection of} sports betting options obtainable all through all of them. Despite not having on-line sports betting obtainable in their state at this cut-off date, these in Maryland are surrounded by a number of|numerous|a selection of} states in which on-line sports betting options are at present obtainable. A number of sportsbooks are already stay in the state, thus we should always|we must always} anticipate to see these similar sportsbooks apply for on-line and mobile betting licenses 코인카지노 to launch in MD in the near future. Here's a quick information for these of you who're new to on-line casinos or might want a refresher on method to|tips on how to} register and use casino apps.

    ReplyDelete