Implementing REST Web services Part 2

Mea Culpa

Yesterday’s post showed a rudimentary WCF Rest service and how we need to change the system.serviceModel tag to support a REST service rather than a SOAP based WCF service. The service returned a string. The reason I set that up was because an object must be serialized prior to being sent back. Originally I thought that had to be done by the developer.

Nope.

The serialization of the object is done by the WCF infrastructure prior to sending it back to the calling function. For this reason there is no need to serialize the object yourself.

The revised interface is as follows;

[ServiceContract]
public interface IUser
{
    [WebGet(UriTemplate = "Login?email={email}&password={password}")]
    [OperationContract]
    [XmlSerializerFormat]
    LoginResult Login(String Email, String Password);
}

and the implementation of the Login method is

public LoginResult Login(string Email, string Password)
{
    RegisteredUserDataSource ds = null;
    String sessionId            = String.Empty;
    LoginResult result          = null;

    try
    {
        result                   = new LoginResult();
        result.Error             = new Error();
        result.Error.ErrorNumber = 0;
        result.Error.Message     = "OK";
        ds                       = new RegisteredUserDataSource();
        sessionId                = ds.LoginUser(Email, ds.EncryptString(Password), HostAddress());

        if (sessionId == String.Empty)
        {
            result.Error.ErrorNumber = 1001;
            result.Error.Message     = "Unrecognised Email and Password combination";
        }
    }
    catch(System.Exception ex)
    {
        result.Error.Message     = ex.Message;
        result.Error.ErrorNumber = 1002;
    }
    finally
    {
        ds               = null;
        result.SessionId = sessionId;
    }

    return result;
}

There are two classes of note here. The first is the Error Object. This will allow us to transfer back to the client the details of any error. This class if defined as follows;

[DataContract]
public class Error
{

    [DataMember]
    public int ErrorNumber
    {
        get;
        set;
    }

    [DataMember]
    public String Message
    {
        get;
        set;
    }
}

At the moment this should be enough to let the caller know what went wrong. It consists of just an error number and a message.

The other object is the LoginResult object. This inherits from a third object called the BaseReturnObject – initially this will just have the Error object, but by doing this it gives us the option of adding more common properties should the need arise.

[DataContract]
public class BaseReturnObject
{
    [DataMember]
    public Error Error { get; set; }
}

and the LoginResult object itself.

[DataContract]
public class LoginResult : BaseReturnObject
{
    [DataMember]
    public String SessionId
    {
        get;
        set;
    }
}

So now we have the object for the method to return.

The Client End

There is not much point in a service if there is nothing to consume it! So now we have to write the code to consume the service that has just been written but before I do lets consider the object that is returned back from the service. I have split this out to a separate module in the project. The reason for this is to enable reuse. The last thing I want to do write two versions of every return object – eventually I can see this being 100’s of objects! By splitting it out I can set a relationship to the project from both the client and the service.

Essentially to call a REST service what we need to do is make a web call to the service and examine the string returned for the results of our call. To find out the format of the call we need to go back to the service code. Below is the declaration of the Login Service

[WebGet(UriTemplate = "Login?email={email}&password={password}")]
[OperationContract]
[XmlSerializerFormat]
LoginResult Login(String Email, String Password);

What we are interested here is the WebGet attribute. This tells us the format of service call for this method and we add the string to the baseUri of the service. Just temporarily I have set a variable to this baseUri but long term it would be better if this came from a configuration setting.

String baseUri = "http://127.0.0.1:8080/User.svc/";

And Later we add to this the address to the service.

url = baseUri + String.Format("Login?email={0}&password={1}", txtEmail.Text, txtPassword.Text);

Now the service is designed to use the QueryString parameter  to represetn the service parameters. I’ve used String.Format to add the required QueryString parameters to the Uri. You will also notice that the parameters denoted by {email}  and  {password} are matched by the calling parameters of the service.

Now we have the address we need to call we can make the call to the service and check the result.

HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
    reader           = new StreamReader(response.GetResponseStream());
    result           = reader.ReadToEnd();
    serializedResult = Server.HtmlDecode(result);
    resultObj        = (LoginResult)Toolbox.Deserialize(typeof(LoginResult), serializedResult);

    if (resultObj.Error.ErrorNumber != 0)
    {
        throw new Exception(resultObj.Error.Message);
    }

}

As this is currently being called from the Web Page it is a synchronous call. Silverlight when I come around to it will need a asynchronous call. First off is to make the request of the method which is what the WebRequest.Create call. Then what we do is check the response with the GetResponse method which returns back and an instance of HttpWebResponse. Because the HttpWebResponse object inherits from IDisposable we can use the ‘using’ construct – which will automatically dispose of the object when we have finished.

Now a response is sent back to the calling function as a stream. We make a call to GetResponseStream to return this stream and then we read the stream into a string.

Now before sending back the results certain characters are replaced with escape sequences – so what we need to do is replace these escape sequences with the correct charactrers – which is why we make a call to the Server.HttpDecode method.

Now we have a string which can be deserialized into a .Net class for easier manipulation.

I have written a static toolbox method to do this for me – just to avoid having to repeat it – making the class and method both static means there is no need to create an instance of the Toolbox class.

The code for the Deserialize method is as follows;

public static Object Deserialize ( Type type, String SerializedObject )
{
    Object resultObj = null;

    ASCIIEncoding encoder       = null;
    MemoryStream ms             = null;
    XmlSerializer serializer    = null;
    XmlTextWriter xmlTextWriter = null;

    encoder = new ASCIIEncoding();
    ms = new MemoryStream(encoder.GetBytes(SerializedObject));
    serializer = new XmlSerializer(type);
    xmlTextWriter = new XmlTextWriter(ms, Encoding.ASCII);
    resultObj = serializer.Deserialize(ms);

    return resultObj;
}

So there we have it – a client to call the REST service.

Advertisements

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