Freigeben über


Async and Await for Http Networking on Windows Phone

Using async and await for executing long-running operations such as networking calls has many benefits: your code is much cleaner and easier to maintain, the result is automatically marshalled back to the originating thread so you don’t need to use Dispatcher.BeginInvoke to get back to the UI thread, and exception handling is simpler to implement. Many of the APIs in the Windows Runtime on both Windows 8 and and Windows Phone 8 use this pattern. It’s the future way of programming asynchronous operations.

Unfortunately, the networking APIs in Windows Phone 8 have not been upgraded to support async and await. On Windows 8 Store Apps by contrast, HTTP networking APIs have been completely re-implemented to offer only async and await methods: the WebClient API has been removed and replaced with a more performant API, HttpClient, and HttpWebRequest has been revamped and now only offers Async methods. However, by use of some extension methods, we can enjoy the goodness of async and await with the WebClient and HttpWebRequest APIs on Windows Phone 8. It will work with Windows Phone 7.5/7.8 projects as well, as long as you add a reference to the NuGet Microsoft.bcl.Async package which adds in async and Task support for Windows Phone OS 7.1.

First, let’s look at HttpWebRequest. The GetResponseAsync extension method for this allow you to write code that is exactly the same as for Windows 8 – great if you are porting code between the two. Kudos to Mathias Shapiro, colleague at Microsoft, who developed this!

 using System.Net;
using System.Threading.Tasks;

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;
        }
    }

    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"; } }
    }
}

Using this, your HttpWebRequest calls now look something like this (don’t forget to add a using WinPhoneExtensions; import to the top of your class!):

         private async System.Threading.Tasks.Task GetSuppliers()
        {
            HttpWebRequest request = (HttpWebRequest)
             WebRequest.Create("https://services.odata.org/Northwind/Northwind.svc/Suppliers");
            request.Method = HttpMethod.Get;
            request.Accept = "application/json;odata=verbose";

            try
            {
                HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();

                Debug.WriteLine(response.ContentType);
                // Read the response into a Stream object.
                System.IO.Stream responseStream = response.GetResponseStream();
                string data;
                using (var reader = new System.IO.StreamReader(responseStream))
                {
                    data = reader.ReadToEnd();
                }
                responseStream.Close();

                var feed = 
                    Newtonsoft.Json.JsonConvert.DeserializeObject<SupplierODataFeed>(data);
                SuppliersList.ItemsSource = feed.d.results;
            }
            catch (Exception ex)
            {
                var we = ex.InnerException as WebException;
                if (we != null)
                {
                    var resp = we.Response as HttpWebResponse;
                    var code = resp.StatusCode;
                    MessageBox.Show("RespCallback Exception raised! Message:{0}" + we.Message);
                    Debug.WriteLine("Status:{0}", we.Status);
                }
                else
                    throw;
            }
        }

…which is exactly the same as you would use in a Windows 8 store app.

We can do a similar thing for WebClient, though here the benefits are simply that the code you end up writing becomes so much cleaner. You don’t get the benefit of compatibility with Windows 8 since WebClient is not available on that platform.

I won’t list the code of the extension methods here, as it’s a bit lengthier than for HttpWebClient. Please download the samples attached to this post to get the extension methods code. When using the extension methods, you end up with lovely clean code like this:

 string url = https://api.twitter.com/1/statuses/user_timeline.xml 
    + "?include_entities=true&include_rts=true&screen_name="
    + nameTextBox.Text + "&count=10";

var client = new WebClient();
string response = await client.DownloadStringTaskAsync(new Uri(url));

If you would like to watch me explaining this (and much, much more!) on video, check out the Networking video in the JumpStart series at http://aka.ms/i5qr0z.

Download the accompanying samples for the code!

Async Await on Phone Samples

Comments

  • Anonymous
    September 23, 2013
    Fantastic. I wish I read this earlier.  It took me a while to just realize that Windows Phone does not support await.I am wondering if you could provide any tips on how to make the numerous Aync methods of WCF services compatible with await on Windows Phone.  Our Windows Phone apps use WCF services extensively and the code is really messy with thoes Aync methods.
  • Anonymous
    September 25, 2013
    Thank you so much, this is exactly what I was looking for and I was unable to find it.I really didn't want to work with WebClient events!
  • Anonymous
    May 27, 2014
    Thanks a lot, you saved me :-)
  • Anonymous
    October 05, 2014
    This proved most helpful - thank you!