How to consume ASP.NET Web API RC with HttpClient?

In previous post I discussed How to consume ASP.NET Web API RC with RestSharp?. RestSharp seems to be a good candidate for working with REST service but during development I found out a problem that RestSharp doesn’t handle enum type very well and I got always null object by posting object to web service. The reason is because ASP.NET Web API serializes enumerated type to integer and RestSharp can only deserialize enumerated type under string format. As consequence you could notice in previous post, the Category property of Product is a kind of enumerate ProductCategories but I have to define it as int so that the serialization works.

public class Product
{
public Guid Id { get; set; }

public int Category { get; set; }

public string Name { get; set; }

public decimal Price { get; set; }
}

Because of disadvantages I would like to use another client which can “really” understand ASP.NET Web API controller and ease my work so that I don’t have to create another object class just to contain same value and serialize enumerated value as string. I decide to use HttpClient, a default one of Microsoft, which should be the “soulmate” for content negotiation with ASP.NET Web API controller.

In this post, I would like to describe how HttpClient works by rewriting all codes of previous post by using HttpClient. The ServiceEngine class will be completely rewritten by using HttpClient.

private HttpClient httpClient;

public ServiceEngine(string url)
{
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(url);
}

Like RestSharp, HttpClient has also its own BaseAddress where you can define the Uri of the web service. However it’s a little weird in this RC version that BaseAddress only uses root of URI. For example if I set BaseAddress as http://localhost/api and I call the resource with HttpClient.GetAsync(“enumproducts/GetAll”), I always get error “Resource not found”. If I look at the URI built by HttpClient, it is “http://localhost/enumproducts/GetAll”. It seems to be that the BaseAddress was trim to only use the root and the controller will be appended to this root.

Therefore unlike RestSharp, the BaseAddress of HttpClient contains only the root path, when we call actions of web service, we must give explicitly the uri from root.

public List GetAll()
{
HttpResponseMessage reponse = httpClient.GetAsync("api/enumproducts/GetAll").Result;
if (reponse.IsSuccessStatusCode)
{
var enumProducts = reponse.Content.ReadAsAsync<List<EnumProduct>>().Result;
return enumProducts;
}
else
return null;
}

The listing above shows how I consume the action GetAll of controller EnumProducts to get all products. The GetAsync function of HttpClient sends a GET request to the specified Uri as an asynchronous operation. This operation will not block program but because we access the Result property of Task. That code row will block and wait for result. It’s same for reading content of response. The ReadAsAsync will block program during reading and deserializing content back to object. For really asynchronous operation we should combine async and await by calling these operations which I may be introduced in next blog. What’s wonderful I would like to show here is that HttpClient can deserialize the category of type enum correctly. I don’t need to define other wrapper to convert enumerated type into string before giving result back.

The other GET functions are same as with RestSharp, we just replace only the RestClient with HttpClient. For POST operation we can use PostAsJsonAsync with parameter of uri and object

public EnumProduct Post(EnumProduct product)
{
HttpResponseMessage reponse = httpClient.PostAsJsonAsync("api/enumproducts/Post", product).Result;
if (reponse.IsSuccessStatusCode)
{
var enumProduct = reponse.Content.ReadAsAsync().Result;
return enumProduct;
}
else
return null;
}

Function PostAsJsonAsync sends a POST request as an asynchronous operation, with a specified value serialized as JSON. Besides posting as Json, we can post as XML or as our custom defined MediaTypeFormatter by

public static Task PostAsync(
this HttpClient client,
string requestUri,
T value,
MediaTypeFormatter formatter,
string mediaType
)

In case if we want to post file to web service, there are small difference between RestSharp and HttpClient. RestSharp provides us a pre-defined AddFile() function which attaches a file into HTTP body request and sends to server. HttpClient doesn’t have this function, to post file or any binary format, we can parse data into StreamContent and attach it in HTTP body. At server site, we just get this stream back and deserialize to format before.

public bool PostFile(string fileName)
{
	if (File.Exists(fileName))
	{
		Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
		StreamContent streamContent = new StreamContent(stream);
		HttpResponseMessage reponse = httpClient.PostAsync(string.Format("api/enumproducts/PostFile/?fileName={0}", fileName), streamContent).Result;

		if (reponse.IsSuccessStatusCode)
		{
			return reponse.StatusCode == System.Net.HttpStatusCode.Created;
		}
		else
			return false;
	}
	else
		return false;
}

For PUT, DELETE operation, HttpClient provides appropriate functions like GET and POST. What we should keep in mind that how ASP.NET Web API handles the media formatter. Like I said in previous post, the basic types should be given through URL, the complex one should be in HTTP body.

public bool Put(Guid productId, EnumProduct product)
{
HttpResponseMessage reponse = httpClient.PutAsJsonAsync(string.Format("api/enumproducts/Put/?productId={0}", productId), product).Result;
if (reponse.IsSuccessStatusCode)
{
var enumProduct = reponse.Content.ReadAsAsync().Result;
return enumProduct;
}
else
return false;
}
public bool Delete(Guid guid)
{
HttpResponseMessage reponse = httpClient.DeleteAsync(string.Format("api/enumproducts/Delete/?productId={0}", guid)).Result;
if (reponse.IsSuccessStatusCode)
{
var enumProduct = reponse.Content.ReadAsAsync().Result;
return enumProduct;
}
else
return false;
}

The HttpClient doesn’t provide additional functions like AddFile() of RestSharp but it allows us to make asynchronous operations through its function and most important is that the content negotiation between HttpClient and ASP.NET Web API works really better. The source code of this post can be downloaded as following link Asp.net Web Api and RestSharp

4 thoughts on “How to consume ASP.NET Web API RC with HttpClient?”

  1. I ran in to a problem similar to what you described with the BaseAddress property seemingly removing the path from my URI when I specified it as something like “http://localhost/api”. However, I was able to get to the bottom of this problem and I wanted to clarify your statement “… it’s a little weird in this RC version that BaseAddress only uses root of URI.”

    This “issue” actually has nothing to do with the HttpClient implementation at all. It has everything to do with the implementation of the Uri class. The problem exists because your base address has no trailing slash. Therefore, when a new Uri is created using the base address and a relative path, the last part of the base address can not be assumed to be a directory and is therefore not used in the new Uri.

    If you simply add a trailing slash to your base address, so that it becomes something like “http://localhost/api/”, then it will work fine and you won’t have to explicitly specify “api/” in your relative paths any longer.

Leave a Reply

Your email address will not be published. Required fields are marked *