Hosting RESTful Web Services in Windows CE – Part II

In the last article we looked at setting up Padarn for using custom HttpHandlers.  In this article we’ll look at exactly how we use a custom handler to get data.


The GET Service


Probably the most common HTTP method is the GET method.  It’s what your browser used to pull the content of this page, and it’s what our REST service will use to get back a list of all Books in our database.


First, I create an entity that defines a Book.  This code file will actually get shared between the device and desktop projects to make things simple.


public class Book
{
  public int ID { get; set; }
  public string Title { get; set; }
  public string Author { get; set; }
  public int? Pages { get; set; }

  public static Book FromXml(string xml)
  {
    // omitted for brevity
  }

  public string AsXml()
  {
    // omitted for brevity
  }
}


I’ve left out the XML serialization/deserialization routines because they take up a lot of space and aren’t really that interesting.  They’re in the example source (I’ll post it when I get these articles up – be patient) if you want to see them.


So next I need a way to get a list of these from my Database.  To encapsulate all of this logic, I created a DatabaseConnector singleton class that resides in the same assembly as the HttpHandlers.  Remember, these handlers are stateless and get created and called every time a URL is requested, so it would be pointless to put a connector instance in the handler itself.


The DatabaseConnector exposes a GetAllBooks method:


public Book[] GetAllBooks()
{
  string sql = “SELECT BookID, Title, Author, Pages FROM Books”;
  List<Book> books = new List<Book>();

  using(SqlCeCommand cmd = new SqlCeCommand(sql, Connection))
  using(var resultset = cmd.ExecuteResultSet(ResultSetOptions.Scrollable))
  {
    if (resultset.HasRows)
    {
      while (resultset.Read())
      {
        books.Add(new Book
        {
          ID = resultset.GetInt32(0),
          Title = resultset.GetString(1),
          Author = resultset.GetString(2),
          Pages = resultset.IsDBNull(3) ? null : (int?)resultset.GetInt32(3)
        });
      }
    }
  }

  return books.ToArray();
}


Now all that I need to do is call this from my GetHandler like so:


public class GetHandler : BaseHandler
{
public override void ProcessRequest(HttpContext context)
{
  string entity = GetEntityName(context.Request.Path);

  // the only entity we support is “Books”
  if (string.Compare(entity, “books”, true) != 0)
  {
    throw new HttpException(HttpErrorCode.NotFound, string.Format(“Entity ‘{0}’ not supported”, entity));
  }

  Book[] books = DataConnector.GetInstance().GetAllBooks();

  StringBuilder sb = new StringBuilder(XML_HEADER);
  sb.Append(“<books>”);

  foreach (var b in books)
  {
    sb.Append(b.AsXml());
  }

  sb.Append(“</books>”);

  context.Response.Write(sb.ToString());
  context.Response.Flush();
}


That’s all there is to it.  We now have implemented a REST service to get all Books from our Windows CE device.  When the Padarn server sees a GET request at http://<ip address>/books/ it will return an XML stream of all of the books in the database.  Here’s an example of the output:


<?xml version=“1.0” encoding=“UTF-8”?>
<books>
  <book>
    <id>1</id>
    <title>The Count of Monte Cristo</title>
    <author>Alexandre Dumas</author>
    <pages>1573</pages>
  </book>
  <book>
    <id>2</id>
    <title>Programming WCF Services</title>
    <author>Juval Loewy</author>
    <pages>610</pages>
  </book>
</books>


The GET Client


Now let’s look at how a non-browser client (i.e. a desktop application) might make use of this service.


First, since I like abstraction and encapsulation, I created a class named RestConnector that handles all communications for REST services and a class named BookClient who’s job is to handle nothing but interactions with the Books service.  RestConnector handles any GET requirements through the following methods:


public string Get(string directory)
{
  string page = string.Format(http://{0}/{1}”, DeviceAddress, directory);

  StringBuilder sb = new StringBuilder();

  byte[] buf = new byte[8192];

  HttpWebRequest request = (HttpWebRequest)WebRequest.Create(page);

  HttpWebResponse response = (HttpWebResponse)request.GetResponse();

  return GetResponseData(response);
}


private string GetResponseData(HttpWebResponse response)
{
  StringBuilder sb = new StringBuilder();

  byte[] buf = new byte[8192];

  Stream stream = response.GetResponseStream();

  string result = null;
  int count = 0;

  do
  {
    count = stream.Read(buf, 0, buf.Length);

    if (count != 0)
    {
      // look for a UTF8 header
      if ((buf[0] == 0xEF) && (buf[1] == 0xBB) && (buf[2] == 0xBF))
      {
        result = Encoding.UTF8.GetString(buf, 3, count 3);
      }
      else
      {
        result = Encoding.UTF8.GetString(buf, 0, count);
      }
      sb.Append(result);
    }
  } while (count > 0);

  return sb.ToString();
}


The BookClient uses the Get method to retrieve the XML for a list of books from the service and then deserializes that XML like this:


public Book[] GetAllBooks()
{
  List<Book> books = new List<Book>();

  string xml = Connector.Get(ENTITY_NAME);

    XmlDocument doc = new XmlDocument();
  doc.LoadXml(xml);

  foreach (XmlNode node in doc.SelectNodes(“books/book”))
  {
    books.Add(Book.FromXml(node.OuterXml));
  }

  return books.ToArray();
}


It’s really that simple.  At this point we have a simple array of Book instances passed from our service to our desktop client so we can do whatever we’d like with them (I’m just displaying them).



Up next: Using POST to add a new entity

3 thoughts on “Hosting RESTful Web Services in Windows CE – Part II”

  1. Send me an email and I can help you out with this. I plan on writing some hands-on labs to cover all of this and I can get you the code for those as an example.

    Like

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