Web API: HTTP Restful Endpoints, The WCF Way (Part II)   Leave a comment

Part I: Overview and Serialization
Part II: WCF Endpoints (This Post)

Part III: Implementing WCF Endpoints

RESTful GET Requests in WCF

WCF for REST provides a strong set of tools to develop HTTP Endpoints in a RESTful manner, optimized for being called by Browsers and returning JSON in the response body.  In this post will will look at how to develop WCF RESTful endpoint to respond to HTTP GET request.  Our emphasis will be on:6732995643_61267eb04f_b

  • Serialization
    • XML
    • JSON
    • Text
  • Header processing
    • Accept Headers
  • Dynamic HTTP Endpoint creation
    • Simple methods to make endpoints visible
  • Ease of Development
    • Consistency
    • Clarity

We will not deal in this post with HTTP POST or with security  (authentication and authorization) issues.  From 10,000 feet the processing tasks preformed during and HTTP GET request are:

  • Authentication
  • Header Processing
  • Passing Control to a a Worker object
    • Authorization
    • return a generic Object To the HTTP Get Processor
  • Error Processing
  • Serialization
    • Based on Accept Header Arguments

IIS (Microsoft’s Web Server) handles HTTP request using HTTP.SYS (IIS 6, IIS 7).  IIS handles requests as a stream the key process in which the key developer hook into the system is with the method call:6733008895_88c6b2e785_b

public Message AllURIs( Message msg ) where Message is defined  as:

using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Xml;

namespace System.ServiceModel.Channels
{
// Summary:  Represents the unit of communication between endpoints in a distributed environment.
public abstract class Message : IDisposable
{…}

}

Note dear reader that Message is an object which represents a SOAP envelope.  RESTful WCF allows us to generate a non-SOAP response type and eliminating the overhead of the XML SOAP envelop and return pure  XML, JSON or Text.  Our task is to create our own ALLURIs method which will return a Message class of our own design.  When a request is in the stream and (our version of) ALLURIs is called the request is running in a particular Context. The following calls make this context available to us:

WebOperationContext webCtx = webCtx = WebOperationContext.Current;
IncomingWebRequestContext requestCTX=webCtx.IncomingRequest;
OutgoingWebResponseContext  outgoingCtx = webCtx.OutgoingResponse;

IncomingWebRequestContext.Method allows us to access what type of request we are processing (GET, POST, etc).

IncomingWebRequestContext.Headers allows us to access the Headers associated with the request.

IncomingWebRequestContext.UriTemplateMatch.QueryParameters allows us to access the query parameters associated with the request.6733010987_52213894d1_b

To process any incoming message our first tasks will be to confirm the request type (i.e. An HTTP GET request) and handle an invalid request type appropriately.  A typical error response is to set the HTTP Status Code to a well defined value and return a null Message object.

if ( requestCTX.Method != “GET” )
{
outgoingCtx.StatusCode = System.Net.HttpStatusCode.BadRequest;

               return null;

}

Accept Headers

Our next task is to process any request Headers which are important to us.  In our case we want to process the Accept Header to see how we will serialize the output object.  We can locate the Accept header several ways, but WCF provides a helper method to ease the process:

IncomingWebRequestContext.Accept

Accept headers contain a list of acceptable formats of the response body (our Message class) as defined my the caller (typically the Browser).  These are known as MIME Types and fall into three categories:  well known types (such as application/xhtml+xml, vendor defined types (like application/vnd.myformat) and the generic “give me anything” type (*/*). An accept header is NOT mandatory and the Accept header may (often will) contain more than one MIME type.  WCF does not provide a helper function to parse the Accept Header but since it is a simple coma delimited string we can call a string.Split function to get an array of string accept values. Once we know what responses the caller (browser) can accept our job is to return our C# object in the proper serialized form.  I will discuss three serialization, JSON (application/json), XML (application/xml+xhtml) and text(text/plain). We assume here that the C# object to be serialized has been annotated with the appropriate DataContract and XML Attributes (See Part I).  Our basic tool is the Message method CreateMessage. This method is overloaded and we will use this form:

public static Message CreateMessage(
	MessageVersion version,
	string action,
	Object body,
	XmlObjectSerializer serializer
)

Let cObject represent the object we wish to serialize our basic calls is:

Message msg=Message.CreateMessage(MessageVersion.None,”*”,cObject,{our serializer goes here});

So our goal now is to create a serializer for each of the types we wish to support. Lets start with the JSON serializer (have you been waiting all this time for this?):

private static XmlObjectSerializer _JSONSerializer( object msg )
{
WebBodyFormatMessageProperty formatter=new WebBodyFormatMessageProperty( WebContentFormat.Json );
OperationContext.Current.OutgoingMessageProperties.Add( WebBodyFormatMessageProperty.Name, formatter );
WebOperationContext.Current.OutgoingResponse.ContentType = “application/json”;
return new DataContractJsonSerializer( msg.GetType( ) );

       }

and now our call then becomes:

Message msg=Message.CreateMessage(MessageVersion.None,”*”,cObject,_JSONSerializer( cObject ));

Our XML serializer is just as simple:

private static XmlObjectSerializer _XMLSerializer( object msg )
{
WebBodyFormatMessageProperty formatter=new WebBodyFormatMessageProperty( WebContentFormat.Xml );
OperationContext.Current.OutgoingMessageProperties.Add( WebBodyFormatMessageProperty.Name, formatter );
WebOperationContext.Current.OutgoingResponse.ContentType = “application/xml+xhtml”;
return new DataContractSerializer( msg.GetType( ) );
}

and now our call then becomes:

Message msg=Message.CreateMessage(MessageVersion.None,”*”,cObject,_XMLSerializer( cObject ));

The only “hard” serializer is for plain text (text/plain). For this we need to first replace the default BodyWriter method with our own:

public class TextBodyWriter : BodyWriter
{
byte[] messageBytes;
public TextBodyWriter( string message )
: base( true )
{
this.messageBytes = Encoding.UTF8.GetBytes( message );
}
protected override void OnWriteBodyContents( System.Xml.XmlDictionaryWriter writer )
{
writer.WriteStartElement( “Binary” );
writer.WriteBase64( this.messageBytes, 0, this.messageBytes.Length );
writer.WriteEndElement( );
}
}

then our text serializer becomes:

public static Message CreateRawMessage( object msg )
{
try
{
string sMsg=msg.ToString( ); //See Note Below
               Message reply=Message.CreateMessage( MessageVersion.None, null, new TextBodyWriter( sMsg ) );
reply.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty( WebContentFormat.Raw );
WebOperationContext.Current.OutgoingResponse.ContentType = “text/plain”;
return reply;
}
catch ( Exception xx )
{
throw new ApplicationException( “CreateRawMessage”, xx );
}
}

and now our call then becomes:

Message msg=Message.CreateMessage(MessageVersion.None,”*”,cObject, CreateRawMessage( cObject ));

Note: In order to make this code as generic as possible I am overloading the ToString method of the C# Object to generate the appropriate text output we want.  Here is our example object from Part I with the ToString method overwritten:6733014367_0beaa22071_b

[DataContract]
public class CObject{
[DataMember]
public string ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List<string> Data { get; set; }
public CObject( ){
Data = new List<string>( );
}

  public string ToString(){

      return string.Format(“ID:{0} Name: {1}”,ID,Name);

   }

}

We can wrap this all up into a base class to make deriving new endpoints as follows:

public class CGetBaseClass
{
public Message AllURIs( Message msg )
{

Message response=null;

//process security here

//process headers here

//call a method to do the actual work

//pass in the headers (in case there are special headers to be processed by the worker method

//pass in the query parameter to be processed by the worker method

//call a virtual function

object thing=GetObject( requestCTX, requestCTX.UriTemplateMatch.QueryParameters);

//call the appropriate serializer here

//return the properly formatted response Message

outgoingCtx.StatusCode = System.Net.HttpStatusCode.OK;

return Message;

}

protected virtual object GetObject( IncomingWebRequestContext requestCTX, NameValueCollection queryParameters )
{
throw new ApplicationException( “GetObject Not Implemented”);
}

}

Note we have make the GetObject method virtual so for any given endpoint which we which to implement we can derive from CGetBaseClass and simply overriding the GetObject method. As:

public partial class GetMySpecialObject : CGetBaseClass
{
protected override object GetObject( IncomingWebRequestContext requestCTX, NameValueCollection queryParameters)
{ /* Your Code Here */   }

}

Ok That gets us up to actually implementing an Endpoint. And that is the story for the next post!


6733016423_4812a29d6c_b




			

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: