Alex Meyer-Gleaves

on httpclient, rest, web services

Introduction to the HttpClient

Introduction

The WCF REST Starter Kit Preview 2 includes a set of classes designed to simplify interaction with REST web services. The communications are performed using the HttpClient class with the help of other types from the Microsoft.Http namespace. These types can be found in the Microsoft.Http.dll assembly located in the Assemblies folder of the starter kit.

Creating a HttpClient

There are three constructors to choose from. There is a default constructor and two others that allow you to set the base address of the web service you will be working with. The base address can be expressed as a String or Uri value.

HttpClient client1 = new HttpClient();
HttpClient client2 = new HttpClient("http://www.foo.com/");
HttpClient client3 = new HttpClient(new Uri("http://www.foo.com/"));

Sending a Message

The Send method of the HttpClient is used to begin a synchronous request. There are also extension methods available to help you configure your request for the different HTTP methods. We will cover those later as they actually use the Send method under the hood.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpResponseMessage response = client.Send(HttpMethod.GET.ToString(), "RestService");

The Send method returns a HttpResponseMessage instance. You can use
the response to check the response status code and retrieve any returned
content. If you pass a URI into the Send method it will be added as a
relative address to the base address.

Validating the Response

There are extension methods defined in the HttpMessageExtensions class that assist you in checking the returned status code. The HTTP status codes are defined in the HttpStatusCode enumeration in the System.Net namespace, which is part of the framework and not the starter kit.

When using the EnsureStatusIsSuccessful extension method an ArgumentOutOfRangeException is thrown if the status code is not HttpStatusCode.OK.

try
{
    HttpClient client = new HttpClient("http://www.foo.com/");
    HttpResponseMessage response = client.Send(HttpMethod.GET, "RestService");
    response.EnsureStatusIsSuccessful();
}
catch (ArgumentOutOfRangeException exception)
{
    Console.WriteLine(exception);
}

You can also use the EnsureStatusIs method to check for specific status codes. You must provide at least one status code to check. If the check fails an ArgumentOutOfRangeException is thrown just the same as for the EnsureStatusIsSuccessful method.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpResponseMessage response = client.Send(HttpMethod.GET, "RestService");
response.EnsureStatusIs(HttpStatusCode.PartialContent, HttpStatusCode.UnsupportedMediaType);

Send Extension Methods

As mentioned earlier, a number of extensions methods are defined in the HttpMethodExtensions class that help you configure your request with the appropriate HTTP method. There are extension methods for the GET, POST, PUT, DELETE and HEAD methods. The extension methods all end up calling the Send method of the HttpClient being extended with the appropriate HttpMethod value to indicate the HTTP method to use.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpResponseMessage postResponse = client.Post("RestService", HttpContent.CreateEmpty());
HttpResponseMessage putResponse = client.Put("RestService", HttpContent.CreateEmpty());
HttpResponseMessage deleteResponse = client.Delete("RestService");
HttpResponseMessage headResponse = client.Head("RestService");

The Post and Put methods require you to provide the content that is to be sent to the server. Providing a null value for the HttpContent parameter will result in an ArgumentNullException being thrown.

Sending Content

The Send method of the HttpClient class, along with the Post and Put extension methods, except a HttpContent instance to represent the content that should be sent to the web service. You create a new instance of the HttpContent class using its static Create method. An empty HttpContent instance can also be created using the static
CreateEmpty method. The Create method can be provided a wide range of parameters including string values, streams, byte arrays and ICreateHttpContent instances.

HttpClient client = new HttpClient("http://www.foo.com/");

HttpContent stringContent = HttpContent.Create("Foo");
HttpResponseMessage response = client.Post("RestService", stringContent);

byte[] bytes = Encoding.UTF8.GetBytes("Foo");
HttpContent byteContent = HttpContent.Create(bytes);
response = client.Post("RestService", byteContent);

using (MemoryStream memoryStream = new MemoryStream(bytes))
{
    HttpContent streamContent = HttpContent.Create(memoryStream);
    response = client.Post("RestService", streamContent);
}

The HttpMultipartMimeForm and HttpUrlEcodedForm classes are examples of classes that implement the ICreateHttpContent interface. These are helper classes that build up the HttpContent instances in more advanced scenarios.

Receiving Content

The content for a response is accessible via the Content property of the HttpResponseMessage. Like the content sent in the request, the property is of type HttpContent. The HttpContent class contains some helper methods for retrieving the data it contains. These include ReadAsByteArray, ReadAsStream and ReadAsString. They return the content in the format that their names suggest.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpResponseMessage response = client.Get("RestService");

byte[] byteArrayContent = response.Content.ReadAsByteArray();
Stream streamContent = response.Content.ReadAsStream();
string stringContent = response.Content.ReadAsString();

Using Headers

HTTP headers play an important role in REST web services. The RequestHeaders class represents the headers that are sent in the request, and the ResponseHeaders class represents the headers returned in the response. Both of these classes derive from the HttpHeaders class. This base class acts like a dictionary for the headers and
exposes properties to access the most common header values.

The static Parse method on the RequestHeaders class can take a string of headers and extract the values. It assumes that the headers appear on separate lines.

const string headers = "Content-Type: text/xml\r\nReferer: http://www.referer.com";
RequestHeaders requestHeaders = RequestHeaders.Parse(headers);

You can also use the properties to set the header values individually, and in a more strongly-typed manner.

RequestHeaders requestHeaders = new RequestHeaders
{
    ContentType = "text/xml", Referer = new Uri("http://www.referer.com")
};

The response headers are accessed via the Headers property of the HttpResponseMessage instance returned from the Send method of the HttpClient, or from one of the extension methods found in the HttpMethodExtensions class.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpResponseMessage response = client.Get("RestService");
Console.WriteLine(response.Headers.ContentType);

Sending Asynchronously

There are two ways of sending a request asynchronously via the HttpClient. The first is to call the BeginSend method and provide an AsyncCallback delegate. The second is to call the SendAsync method and subscribe to the SendCompleted event. You must prepare a HttpRequestMessage instance to provide to both of these methods as they do not provide the same overloads as the Send method.

The BeginSend method uses the typical IAsyncResult pattern. This allows you to continue with other work and have the callback delegate invoked, or wait for the result using a wait handle or through polling. The example below continues execution leaving the AsyncCallback delegate to be invoked when the request has completed. The EndSend method on HttpClient is called with the IAsyncResult provided to the callback to get access to the response message.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.GET.ToString(), "RestService");
AsyncCallback callback = asyncResult =>
     {
         HttpResponseMessage response = client.EndSend(asyncResult);
         Console.WriteLine(response.StatusCode);
     };
client.BeginSend(request, callback, null);

The SendAsync method allows you to send requests asynchronously and be informed when a request has completed via the SendCompleted event. The event argument is of type SendCompletedEventArgs and exposes Request and Response properties. As expected, these properties provide access to the original HttpRequestMessage, and the returned
HttpResponseMessage. There is also a UserState property, which is actually defined on the base SendCompletedEventArgs, that can be used to return an object that was passed into the SendAsync method. The user state can be used as a key of sorts to match up returning responses with the requests that initiated them.

HttpClient client = new HttpClient("http://www.foo.com/");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.GET.ToString(), "RestService");
client.SendCompleted += (s, e) => Console.WriteLine(e.UserState);
const string userState = "foo";
client.SendAsync(request, userState);

Summary

The HttpClient provides a simple and clean API for working with REST web services. It removes a great deal of the complexity that is inherent with working so close to the HTTP stack. All the HTTP methods are supported, including the common GET, POST, PUT and DELETE methods. Building content for your requests and extracting content from responses has been greatly simplified. The common HTTP headers for the request andresponse can be accessed through a strongly-typed interface. You can check the status of responses without having to worry about remembering the numeric HTTP status codes. There are two different patterns available for performing asynchronous requests.

profile
AUTHOR

Alex Meyer-Gleaves

I'm a Technical Architect living in Australia (that island like continent in the southern hemisphere). I love Microsoft .NET and C#. I hate early mornings, slow drivers and Lotus Notes.