With arrival of HttpClient in .Net 4.5 one would think that consuming rest services with async should be a breeze in portable class libraries. Unfortunately, HttpClient was not added for PCL (nor for windows phone). We are left with using WebClient and HttpWebRequest.
Preparing HttpWebRequest for async
We start off with creating an interface for abstracting our service like this:
public interface IServiceClient where T : class
{
Task Get(string id);
Task<IList> GetAll();
Task Post(T dto);
Task Put(T dto);
}
Consuming REST Get
We start off by implementing the GetAll method. I prefer using Json.Net due it handling object trees better than MS own implementation.
public async Task<IList> GetAll()
{
var response = await this.MakeAsyncRequest();
var result = JsonConvert.DeserializeObject<IList>(response);
return result;
}
This method does little more than converting the string callback from MakeAsyncRequest into a valid object. MakeAsyncRequest accepts an url appending parameter and returns a Task. The method makes use of the Task.Factory.FromAsync to call the REST service asynchronously, returning the Task followed by a continuation to read the WebResponse.
private Task MakeAsyncRequest(string urlAppend = "")
{
var request = (HttpWebRequest)WebRequest.Create(this.url + urlAppend);
request.ContentType = "application/Json";
return Task.Factory.FromAsync(request.BeginGetResponse, asyncResult => request.EndGetResponse(asyncResult), null)
.ContinueWith(t => this.ReadFromStreamResponse(t.Result));
}
Implementing getting a single item by id is then simple:
public async Task Get(string id)
{
string response = await this.MakeAsyncRequest(id);
var result = JsonConvert.DeserializeObject(response);
return result;
}
We can now use a REST service from the view model without locking the UI thread by using the await keyword.
Consuming REST Post/Put
Writing the post/put requires a bit more plumbing. Trying to keep the code clean our intitial Post method looks like this:
public async Task Post(T dto)
{
var postData = JsonConvert.SerializeObject(dto);
var request = (HttpWebRequest)WebRequest.Create(this.url);
request.ContentType = "application/Json";
request.Method = "POST";
return await CreatePostWebRequestTask(request, () => Encoding.UTF8.GetBytes(postData));
}
It creates a string presentation of our Dto, creates the web request object and then ships it off async. The Put method would look exactly the same, but we would use “PUT” as request method:
request.Method = "PUT";
Our CreatePostWebRequestTask starts off by sending the async request, waits for the result to come back and then de-serialize it.
public static Task CreatePostWebRequestTask(HttpWebRequest request, Func<byte[]> dataGetter)
{
var task = CreatePostWebRequestTask(request, dataGetter);
task.Wait();
return Task.Factory.StartNew(() => DeserializeObjectFromResult(task));
}
private static T DeserializeObjectFromResult(Task<byte[]> task)
{
var buffer = Encoding.Convert(Encoding.GetEncoding("iso-8859-1"), Encoding.UTF8, task.Result);
var tempString = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
return JsonConvert.DeserializeObject(tempString);
}
The async request itself looks like this:
private static Task<byte[]> CreatePostWebRequestTask(HttpWebRequest request, Func<byte[]> dataGetter)
{
return Task<byte[]>.Factory.StartNew(
() =>
{
//Exception exception = null;
byte[] responseData = null;
var getRequestStreamTask =
Task.Factory.FromAsync(request.BeginGetRequestStream,
request.EndGetRequestStream,
null)
.ContinueWith(task => WriteDataToOutStream(dataGetter, task));
getRequestStreamTask.Wait();
Task requestTask =
Task.Factory.FromAsync(
request.BeginGetResponse,
request.EndGetResponse,
null,
TaskCreationOptions.AttachedToParent)
.ContinueWith(task => responseData = GetData(task.Result));
requestTask.Wait();
return responseData;
},
TaskCreationOptions.AttachedToParent);
}
private static void WriteDataToOutStream(Func<byte[]> dataGetter, Task task)
{
var outStream = task.Result;
var data = dataGetter();
if (data == null || data.Length == 0)
{
return;
}
outStream.Write(data, 0, data.Length);
}
private static byte[] GetData(WebResponse response)
{
using (response)
{
var inStream = response.GetResponseStream();
if (inStream != null)
{
using (inStream)
{
return GetResponseData(inStream, response.ContentLength > 0 ? response.ContentLength : 1024);
}
}
}
return null;
}
private static byte[] GetResponseData(Stream inStream, long bufferSize)
{
using (var output = new MemoryStream())
{
int count;
do
{
var buf = new byte[bufferSize];
count = inStream.Read(buf, 0, Convert.ToInt32(bufferSize));
output.Write(buf, 0, count);
}
while (inStream.CanRead && count > 0);
return output.ToArray();
}
}
Summary
This should help you along to getting a nice re-usable library using REST services and async in the absence of HttpClient. Code with examples can be found here http://tinyurl.com/awwg9l3.
*Update* By the time I finished this blog, it seems MS finally got their act together and made HttpClient for Windows Phone and Portable Class Libraries as well. See Portable HttpClient for .NET Framework and Windows Phone.Its still in beta, so those who want to stay away from beta code are free to use the example code above.