Condividi tramite


Windows 8 / Windows Phone Code Sharing: HttpWebRequest GetResponseAsync

I’m working on building helpers for sharing code between Windows 8 (Windows Store) and Windows Phone. Keep an eye on my github for additional updates.

I love the new async-await model in .NET 4.5 (and also in .NET 4.0 if you’re interested). One of the most exciting uses of it (for me) is the ability to do HTTP calls and simply wait for the result. Instead of setting up event handlers and custom events I can write a service that looks like

 Tweet myTweet = await GetTweet(tweetID);

and just wait for the result to come back. If I’m writing a service call for a Windows Store app and I use HttpWebRequest to make my call, the writing this call is very simple

 public async Task<string> GetMyData(string urlToCall)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlToCall);
    request.Method = HttpMethod.Get;
    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();

    using (var sr = new StreamReader(response.GetResponseStream()))
    {
        return sr.ReadToEnd();
    }
}

and then I can run this as an await-able method.

But if we want to share code between my Windows 8/Windows Store app and my Windows Phone app, the problem comes where this await-able GetResponseAsync method isn’t available in Windows Phone. We’re stuck using the old event based model.

So I threw together these extensions for solving that problem. These are actually two problems here:

  1. There is no awaitable GetResponseAsync() in Windows Phone’s version of HttpWebRequest
  2. There is no HttpMethod enum in Windows Phone. We have to use a string instead.

So I wrote the following so we can write the same http service calls one time and share it across our Windows 8 and Windows Phone projects.

First, an extension for Windows Phone HttpWebResponse:

 namespace WinPhoneExtensions
{
    public static class HttpExtensions
    {
        public static Task<HttpWebResponse> GetResponseAsync(this HttpWebRequest request)
        {
            var taskComplete = new TaskCompletionSource<HttpWebResponse>();
            request.BeginGetResponse(asyncResponse =>
            {
                try
                {
                    HttpWebRequest responseRequest = (HttpWebRequest)asyncResponse.AsyncState;
                    HttpWebResponse someResponse = (HttpWebResponse)responseRequest.EndGetResponse(asyncResponse);
                    taskComplete.TrySetResult(someResponse);
                }
                catch (WebException webExc)
                {
                    HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
                    taskComplete.TrySetResult(failedResponse);
                }
            }, request);
            return taskComplete.Task;
        }
    }
}

This takes the event-based model that we use in Windows Phone and encapsulates it in a Task so we can use it in an await-async manner.

Second, I added this class to the same namespace to deliver an HttpMethod class that works with the same code that we would use in Windows 8.

 public static class HttpMethod
{
    public static string Head { get{return "HEAD";} }
    public static string Post { get{return "POST";} }
    public static string Put { get{return "PUT";} }
    public static string Get { get{return "GET";} }
    public static string Delete { get{return "DELETE";} }
    public static string Trace { get{return "TRACE";} }
    public static string Options { get{return "OPTIONS";} }
    public static string Connect { get{return "CONNECT";} }
    public static string Patch { get{return "PATCH";} }
}

Now, to use this with our code above, we only need to add:

 #if WINDOWS_PHONE
using WinPhoneExtensions;
#endif

to our class that makes the Http call and we’re good to go!

Comments

  • Anonymous
    January 03, 2013
    Great article! Thank a lot, it was very helpful for my.

  • Anonymous
    January 24, 2013
    Thanks ! Very useful !

  • Anonymous
    March 20, 2013
    This will fail at runtime for Store if you ever need to set certain HTTP headers like content-length or user-agent - beware...