Tuesday, December 15, 2009

Consume RestFul Service as Object using WCF.


As an enterprise software engineer, i deal with disparate systems. I deal with several impedance mismatches between these systems, i built several integration components to glue these systems together. Many of these systems have its domain semantics (They speak different languages ), my daily work life activities is to build abstractions over semantics of different and disparate systems. I am one of those software engineers that believe in domain engineering paradigm , separation of concerns (A dog is a dog not a dogcat) , inversion of control e.t.c I enjoy spending time on a solution to see how it could be done better.My daily work is so challenging that bringing systems together from multiple domain is my strength and i have used aspect orientation to my advantage.

Recently, i entered a problem, we have an application that exposes objects as pure xml , the application supports soap 11 . The soap envelope does not have an action (Simply put in in WCF terms, it does not have an OperationContract , so when you consume this service, there is no web service operations to call on it ), This application only accepts pure XML as request and returns xml as response.

This is chaos, i know some hackers would say there is no biggy here, just construct an string as xml, send it over to the REST service and get the response as string and use linq or manually tranverse the string and construct an object from it. Wao! this indeeed is a hacky packy solution (Forgive me hackers brothers, i do not intend to cross the line). We are now in the world of objects, things have changed, newer platforms have emerged.

The diagram above speaks more of my scenarios, XML input and XML output using the good old REST Service approach. How am i suppose to translate these XML returned as string into object , how does WCF Channel Factories come into play with these. Keep focus, i will explain how i did the magic ...

First : Call the service from web browser , or get the schema of all the objects returned by the service. I chose the first option for my scenario as there was no schema, so what did i do, i called the web service from a browser , saved it as anything.xml , then i opened visual studio command prompt , ran the XSD.EXE command using the following parameters :

xsd anything.xml /outputdir:mydir



The command above, generates the schema from the xml which i saved from the browser called anything.xml. Now that i have my .xsd file handy, i can generate a .net class using the same xsd.exe command but with different parameters. Thats the price to pay.

Now i have anything.xsd in my output directory , i would like to generate .net classes using the follwoing command :

xsd /c anything.xsd

This generates C# classes for me. Now i am equiped with the object representations of the XML that the REST service sends to my consuming applications.

Second : How do i send request and recieve response from the REST Service ?

To do this, you would need to understand how to use low level WCF communication classes to send soap messages back and fort. First i want to have a mechanism that will enable me to serialize and de-serialize my soap messages Request messages : The following is an example request object :

Sample XML Request

<hellorest>
<message>Get Hello world </message>
</hellorest>

Now that we have carefully reviewed the generated c-sharp class we can proceed to creating the request object. Since we have the request xml above, we will have to hand code this as a c-sharp class. Using the good old System.Xml.Serialization namespace, we would decorate our request class with xml attributes for pure serialization. Here is our sample request object and a Serialization method :


[XmlRoot( "hellorest" )]
public class RESTWebRequest
{
[XmlElement( "Message" )]
public string Message
{
get;
set;
}

public RESTWebRequest( )
{

}

public XmlElement SoapSerialization( )
{
XmlSerializer serializer = new XmlSerializer( typeof( RESTWebRequest ) );
StringWriter writer = new StringWriter( );

serializer.Serialize( writer , this );

XmlDocument xmlDocument = new XmlDocument( );
xmlDocument.Load( new StringReader( writer.ToString( ) ) );

return xmlDocument.DocumentElement;
}
}


Having done that, we are ready to use the communication classes within WCF from the lower level to send our request object and receive our response as object too. To do this, we need to understand the following classes with the System.ServiceModel namespace :

1. Message : Wraps the request/response in a soap envelope.
2. BasicHttpBinding : Communication ensured via HTTP .
3. IRequestChannel
4. IChannelFactory

The listed interfaces and classes would be used to send and recieve , serialize and de-serialize soap messages. Now the long awaited class, the RestAgent which will be used to send and recieve messages is defined below :





internal class RESTAgent : IDisposable
{
IChannelFactory channel;

internal string EndPoint { get; set; }

internal RESTAgent( string EndPoint )
{
BasicHttpBinding basicHttpBinding = new BasicHttpBinding( );
basicHttpBinding.MaxReceivedMessageSize = 1000;
this.EndPoint = EndPoint;

channel = basicHttpBinding.BuildChannelFactory
(
new BindingParameterCollection( )
);

channel.Open( );
}

internal GeneratedFromXSD GetResponset( RESTWebRequest webRequest )
{
Message requestMessage = CreateIncomingMessage( webRequest );
IRequestChannel requestChannel = GetRequestChannel( );

requestChannel.Open( );

//Hey. time out here is ugly.
Message responseMessage = requestChannel.Request( requestMessage , new TimeSpan( 1 , 20 , 00 ) ); //1 hour

requestMessage.Close( );

GeneratedFromXSD anything = DeserializeXMLStream( GetMessageContent( responseMessage ) );

responseMessage.Close( );

return anything;
}

private GeneratedFromXSD DeserializeXMLStream( string xmlStream )
{
XmlSerializer serializer = new XmlSerializer( typeof( GeneratedFromXSD ) );

return ( GeneratedFromXSD ) serializer.Deserialize( new StringReader( xmlStream ) );
}

private string GetMessageContent( Message message )
{
XmlDictionaryReader xmlReader = message.GetReaderAtBodyContents( );

return xmlReader.ReadInnerXml( );
}

private System.ServiceModel.Channels.Message CreateIncomingMessage( RESTWebRequest request )
{
XmlNodeReader reader = new XmlNodeReader( request.SoapSerialization( ) );
return Message.CreateMessage( MessageVersion.Soap11 , "" , reader ); //Give us the real thing. Wrap up with soap envelope without a specific action = ""
}

private IRequestChannel GetRequestChannel()
{
return channel.CreateChannel( new EndpointAddress( EndPoint ) );
}

public void Dispose( )
{
channel.Close( );
}
}


You can use the RESTAgent class to send and recieve soap message from our Restful service by using the follwoing :



RESTWebRequest request = new RESTWebRequest();
request.Message = "Hello world";

RESTAgent agent = new RESTAgent( "http://localhost/restapi" ); //Parameter is the endpoint address

GeneratedFromXSD anything = agent. GetResponset( request );