A REST Client Library for .NET, Part 1

Recently, a poster on the ASP.NET XML Web Services Forum asked how to invoke a particular REST-based web service and get the returned XML. Since this sort of question has been asked many times before, I thought I’d try to answer it with some code.

The poster wanted to access one of the web services of EarthTools™, by Jonathon Stott. The time zone or local time service is invoked by issuing an HTTP “GET” request to a URL that includes the latitude and longitude. The response is an XML document containing information about the time zone at that location. This is very easy to do with .NET, by using the System.Net.WebRequest class. The remainder of this article describes how I created a simple console application to invoke the service. In the next part of this series, I’ll describe how I then refactored that application to produce the first draft of a library that can be used to call other REST-based web services.

Main Program

I started by creating a C# Console application in Visual Studio 2008. I named it WebRequestClient, so that became the default namespace.  Following my typical pattern for a sample application, I modified Program.cs as follows:

Program.cs
using System;

namespace WebRequestClient
{
    internal class Program {
        private static void Main(string[] args)
        {
            var latitude = double.Parse(args[0]);
            var longitude = double.Parse(args[1]);
            var client = new TimeZoneClient();
            var result = client.GetTimeZone(latitude, longitude);

            Console.WriteLine(
                "version={0}, localtime={1}, offset={2}, suffix={3}",
                result.version, result.localtime,
                result.offset, result.suffix);
            Console.Write("ENTER to exit:");
            Console.ReadLine();
        }
    }
}

All the real work is performed in the TimeZoneClient class. This pattern permits me to create the code functionality in a simple console application, yet isolates the “user interface” from the “business logic” so that I can later substitute a Windows Forms or ASP.NET user interface, if warranted.

TimeZone Client – Original Version
using System;
using System.Net;
using System.Xml.Serialization;

namespace WebRequestClient
{
    internal class OriginalTimeZoneClient {
        private const string BASE_URL = "http://www.earthtools.org/timezone";
        private const string REQUEST_URL_FORMAT = "{0}/{1}/{2}";

        public timezone GetTimeZone(double latitude, double longitude)
        {
            var uriString = String.Format(REQUEST_URL_FORMAT, BASE_URL, latitude, longitude);
            var requestUri = new Uri(uriString);

            var request = WebRequest.Create(requestUri);
            using (var response = request.GetResponse())
            {
                using (var responseStream = response.GetResponseStream())
                {
                    var ser = new XmlSerializer(typeof (timezone));
                    var result = (timezone) ser.Deserialize(responseStream);
                    return result;
                }
            }
        }
    }
}

 

First, the URL is computed. For a latitude and longitude of 35.66 and 95.36, the URL would be http://www.earthtools.org/timezone/35.66/95.36. I then create a System.Uri instance from the constructed string. Whenever you have a choice between using an API that accepts the string form of a URL and System.Uri, you should use System.Uri. The constructor parses the URI string, and will throw an exception if the format is incorrect. It is better to have that exception thrown at an obvious location (the constructor call), rather than deep inside the API, which will eventually call the System.Uri constructor if you haven’t already done so.

Issuing the Request

The System.Net.WebRequest class is the abstract base class used for processing request/response interactions to a server, based on the URL. It defines the static Create factory method that is used to create an instance of the appropriate derived request class. In this example, since the URL uses the “http:” scheme, an instance of the System.Net.HttpWebRequest class is created. In this particular example, the defaults are used for all of the properties of the WebRequest, but one can set the Timeout, Proxy, Credentials, and other properties of the request before the request is issued.

The request is only issued when the GetResponse method is called. It returns an instance of the appropriate System.Net.WebResponse derived class. Since this class implements the System.IDisposable interface, the GetResponse method is called in a using block. This ensures that the Dispose method is called when the WebResponse is no longer needed, permitting the network connection to be closed. If the request resulted in an HTTP error code (4xx or 5xx), then a WebException would be thrown.

If the request is successfully issued, then the code calls the GetResponseStream method. this returns a System.IO.Stream instance that can be read from in order to return the body of the response. In this case, that body will contain the response XML. If I had wanted to process the XML directly, I would have loaded the stream into an XmlDocument or XPathDocument, or I would have used the stream to create an XmlReader over the bytes of the response.

The XML Schema

The EarthTools™ site provides an XML schema that describes the results. Unfortunately, although the provided schema accurately describes the individual elements being returned, it does not describe the response as a whole. I’ve modified that schema to reflect the entire message. I loaded the original schema into Internet Explorer by typing its URL into the address bar (http://www.earthtools.org/timezone-1.1.xsd). I then used the “File->Save As” command to save the schema to the project directory. I then added it to the project by using the “Add->Existing Item” command. I then modified the schema by adding the “timezone” element to describe the entire document, referring to the elements already defined in the schema. The resulting schema is as follows:

Modified timezone-1.1.xsd
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="version" type="xs:string"/> <xs:element name="location"> <xs:complexType> <xs:sequence> <xs:element name="latitude" type="xs:string"/> <xs:element name="longitude" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="offset" type="xs:string"/> <xs:element name="suffix" type="xs:string"/> <xs:element name="localtime" type="xs:string"/> <xs:element name="isotime" type="xs:string"/> <xs:element name="utctime" type="xs:string"/> <xs:element name="dst" type="xs:string"/> <xs:element name="timezone"> <xs:annotation> <xs:documentation> This element was added to describe the overall structure of the XML response.
        The original schema consists of all elements above the timezone element.
      </xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element ref="version"/> <xs:element ref="location"/> <xs:element ref="offset"/> <xs:element ref="suffix"/> <xs:element ref="localtime"/> <xs:element ref="isotime"/> <xs:element ref="utctime"/> <xs:element ref="dst"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>

Deserializing The Result

I wanted to do a little more than the poster had requested. Rather than returning the raw XML, I wanted to use XML serialization to return an instance of a class that corresponds to the XML schema. But, first, I had to create that class, based on the XML Schema. To do this, I used the XSD.EXE tool included with Visual Studio. I started a “Visual Studio 2008 Command Prompt” window available in the “Visual Studio Tools” folder of the “Visual Studio 2008” start menu folder. I set my default to the folder containing the project, then issued a simple command:

Using XSD.EXE to Create Classes from XML Schema
WebRequestClient>xsd /c /n:WebRequestClient timezone-1.1.xsd
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.3038]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file ‘WebRequestClient\timezone-1_1.cs’.

WebRequestClient>

This simply tells XSD.EXE to create classes from the schema (/c), and to use “WebRequestClient” as the namespace for those classes. XSD.EXE creates the file “timezone-1_1.cs”, which I added to the project. This includes the “timezone” type into which I deserialized the response:

Deserializing the Stream
var ser = new XmlSerializer(typeof (timezone));
var result = (timezone) ser.Deserialize(responseStream);
return result;

Conclusion

Nothing more is necessary for this simple example. It issues the request, receives the response and a Stream against the body of the response, and finally deserializes that stream into a result object. This pattern can be followed for any REST web service that uses the HTTP “GET” method and which has an XML Schema that describes the results.

With a little bit of editing, you can have a separate copy of this code for each service and result type you need to process. In the next part of this series, I’ll discuss how to refactor this code so that you won’t need to have multiple copies to maintain.

Technorati Tags: ,
About these ads
This entry was posted in Web Services. Bookmark the permalink.

2 Responses to A REST Client Library for .NET, Part 1

  1. Des says:

    Hi

    Getting the following error from the responseStream

    “There is an error in XML document (2, 2).”

    Des

  2. I suggest that you use a tool like Fiddler (http://www.fiddler2.com/) to look at the response.

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