Bill Reiss is a Windows Platform Development MVP and a Senior Consultant for AgileThought

Making JSON Web Requests easier with async and await

Source code for this sample: http://www.billreiss.com/wp-content/uploads/2013/01/TestJsonWebAsync.zip

The application landscape of today often consists of applications on various different platforms accessing a common set of web services. By far the most common technique for doing this, since it’s well supported on pretty much every platform, is to use REST and JSON. If recommending a service layer to a customer these days, I’ll almost always recommend REST and JSON, with JSON being the more important part of the equation. It gives you the flexibility to decide later if you want to support other client side platforms and to be ready for any future platforms that may arise.

Now with fairly little effort you can code up an HttpWebRequest and do JSON serialization and deserialization using the built in .NET classes, but you have to deal with callbacks and probably more code than is necessary to handle the JSON serialization and deserialization. Also if you have to call multiple services in sequence, the nesting of the different calls and async callbacks gets nasty really fast. With the advent of the new async and await keywords in C#, using HttpWebRequest with callbacks seems so antiquated. In this article I’ll provide a sample of how to easily call JSON based web services using the async and await pattern. This sample is for Windows 8 Store apps, but it would also work with Windows Phone 8.

Setting Up

To follow along you’ll need Windows 8 and Visual Studio 2012. We’ll do File->New Project and select C#->Windows Store->Blank App(XAML). Name it TestJsonWebAsync.

image

Now we’ll add new project to the solution, of type Class Library (Windows Store Apps) and name it JsonWebAsync. This will be a class library you can reuse from your Windows 8 apps. You could potentially use a Portable Class Library (PCL) assembly, but there are currently problems with the Windows Phone 8 designer in Visual Studio and PCL, so if you want to target both platforms you’ll currently need 2 separate projects anyway, and just add all of the same files to the other project.

image 

In the TestJsonWebAsync project, add a reference to the JsonWebAsync project so that we can call it from there.

image

In the JsonWebAsync project, add a class named JsonWebClient. This will contain the wrapper methods we’ll create to call JSON based services.

First let’s create a method that returns the result string, and in this case we won’t care what the format is. Actually we’ll create two methods, one that takes an HttpWebRequest object so that you can specify other options on the request like custom headers or authentication, and another that just takes a URL. Add the following methods to the JsonWebClient class, and make sure you make the class public:

public class JsonWebClient
{
    public async Task<System.IO.TextReader> DoRequestAsync(WebRequest req)
    {
        var task = Task.Factory.FromAsync((cb, o) => ((HttpWebRequest)o).BeginGetResponse(cb, o), res => ((HttpWebRequest)res.AsyncState).EndGetResponse(res), req);
        var result = await task;
        var resp = result;
        var stream = resp.GetResponseStream();
        var sr = new System.IO.StreamReader(stream);
        return sr;
    }

    public async Task<System.IO.TextReader> DoRequestAsync(string url)
    {
        HttpWebRequest req = HttpWebRequest.CreateHttp(url);
        req.AllowReadStreamBuffering = true;
        var tr = await DoRequestAsync(req);
        return tr;
    }
}

Now these methods allow us to await on the request and returns a TextReader that we can use to read the result. This is an improvement over the HttpWebRequest as-is because we don’t have to worry about callbacks, etc. We can add code to our test app to see what comes back from these methods.

For this test we’ll retrieve the JSON string for the Microsoft page on Facebook. First in MainPage.xaml, inside the Grid element, add the following:

<StackPanel>
    <Button Content="Get JSON string from Microsoft's Facebook page" x:Name="buttonStringGet" Click="buttonStringGet_Click_1"/>
    <TextBlock x:Name="resultText" Style="{StaticResource BodyTextStyle}" TextWrapping="Wrap"/>
</StackPanel>

Then we need to code up the click event in MainPage.xaml.cs:

private async void buttonStringGet_Click_1(object sender, RoutedEventArgs e)
{
    JsonWebAsync.JsonWebClient client = new JsonWebAsync.JsonWebClient();
    var resp = await client.DoRequestAsync("http://graph.facebook.com/microsoft");
    string result = resp.ReadToEnd();
    resultText.Text = result;
}

It’s a neat little trick that for most events in Windows 8 Store apps, you can just add the async keyword and it will then be an async method. This allows you to then await inside of the method, since methods have to be tagged async for you to use the await keyword.

If we run this and click the button, it looks like this:

image

However it’s not quite where we want it to be, what we want is to get the data back as a strongly typed object so that it’s easier to deal with.

Now you can get away with using the built in .NET JSON support but I really love Json.NET, an open source library by NewtonSoft. You can serialize and deserialize JSON easily with one line of code. You can go to CodePlex and download the latest assemblies and add the references to your project, but it’s even easier with NuGet.

In Solution Explorer if you right click on the project node for JsonWebAsync you can choose Manage NuGet Packages. Json.NET will probably be on the first page because it’s really popular but if not you can enter JSON in the search box. Select Json.NET and click Install.

image

If you’re not familiar with NuGet, it will add all of the assemblies you need, and any supporting assemblies if necessary (and get the correct versions!), so after selecting this you should be able to use Json.NET in this class library. Do the same thing for our TestJsonWebAsync project, since for our next test we’ll want to access Json.NET in the test app.

Let’s say you want to be able to just retrieve JSON content and get property values based on the name of the property. This allows you to not have to define a proxy object in the client side code to get at deserialized JSON data. It’s very similar to how you would deal with JSON in JavaScript.

To kick off the test, we’ll add another Button to the StackPanel under the first one in MainPage.xaml:

<Button Content="Get JObject from Microsoft's Facebook page" x:Name="buttonJObjectGet" Click="buttonJObjectGet_Click_1"/>

Then for the event handler, we’ll do the following:

    private async void buttonJObjectGet_Click_1(object sender, RoutedEventArgs e)
    {
        JsonWebAsync.JsonWebClient client = new JsonWebAsync.JsonWebClient();
        var resp = await client.DoRequestJsonAsync("http://graph.facebook.com/microsoft");
        string result = "about property value is: " + resp["about"];
        resultText.Text = result;
    }

When we click on the second button, the JSON response is parsed into a JObject (provided by Json.NET to allow easy name/value pair access) and we set the result to the about property.

image

This is a really good step in the right direction, but if you’re an experienced .NET developer this whole name/value pair thing probably rubs you a little the wrong way. So let’s see how we can return a strongly typed object. These methods are very similar to the previous methods, but in this case we’ll call the generic version of DeserializeObject and add a type specifier to the method call:

    public async Task<T> DoRequestJsonAsync<T>(WebRequest req)
    {
        var ret = await DoRequestAsync(req);
        var response = await ret.ReadToEndAsync();
        return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(response);
    }

    public async Task<T> DoRequestJsonAsync<T>(string uri)
    {
        var ret = await DoRequestAsync(uri);
        var response = await ret.ReadToEndAsync();
        return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(response);
    }

Now in the test app, we’ll need another button. Add it after the previous one.

    <Button Content="Get Strongly typed object from Microsoft's Facebook page" x:Name="buttonTypedObjectGet" Click="buttonTypedObjectGet_Click_1"/>

Since this is going to return a strongly typed object, we need to define one in our test program. We could go field by field in the JSON object and create corresponding fields, but thankfully there are a few apps and services that can help us out. If you “google it with bing” and search for “generate c# from json” you’ll see a bunch of options but let’s use the free online web version at http://json2csharp.com/

On this site enter the URL to our service, http://graph.facebook.com/microsoft and it’ll look something like this:

image

In the TestJsonWebAsync project, let’s add a new class called FacebookPageResponse. In the json2csharp page, click the copy button and then Ctrl-C. Delete the FacebookPageResponse class definition in the FacebookPageResponse.cs file and then replace it with the contents of the clipboard. Rename RootObject to FacebookPageResponse. Now this object can be used when deserializing a JSON object.

Here is the button event handler you need to add to MainPage.xaml.cs:

    private async void buttonTypedObjectGet_Click_1(object sender, RoutedEventArgs e)
    {
        JsonWebAsync.JsonWebClient client = new JsonWebAsync.JsonWebClient();
        var resp = await client.DoRequestJsonAsync<FacebookPageResponse>("http://graph.facebook.com/microsoft");
        string result = "about property value is: " + resp.about;
        resultText.Text = result;            
    }

Notice that it’s almost identical to the previous version but now we specify the object type on the method call and access the property directly instead of getting it by name. Here is how it looks when executed:

image

Up until this point we’ve dealt with simple string URLs in our tests. This isn’t always the case because many times you have to provide authentication, specify a different REST verb, or specify other headers. In these cases you can use the method overload that accepts an HttpWebRequest instead of a URL. To test this, the official web site for .NET User Groups in Florida, http://fladotnet.com, provides a JSON interface but to use it you need to specify a content type. In the test app let’s add yet another button.

    <Button Content="Get JArray from Florida.net" x:Name="buttonJObjectFloridaGet" Click="buttonJObjectFloridaGet_Click_1"/>

Then for the event handler:

    private async void buttonJObjectFloridaGet_Click_1(object sender, RoutedEventArgs e)
    {
        JsonWebAsync.JsonWebClient client = new JsonWebAsync.JsonWebClient();
        HttpWebRequest req = HttpWebRequest.CreateHttp("http://www.fladotnet.com/FlaNetData/api/flnetmeetings");
        req.ContentType = "application/json";
        var resp = await client.DoRequestJsonAsync<Newtonsoft.Json.Linq.JArray>(req);
        string result = "first description is: " + resp[0]["Description"];
        resultText.Text = result;
    }

In this case the result is an array of JSON objects, so we can call the generic DoRequestJsonAsync method and specify a type of JArray and get the result to deserialize properly. Then we get the first entry and show its Description property. We could have also generated a strong type and serialized into it as we did in the previous example.

image

I hope this has been useful and you can see how powerful and simple mixing HttpWebRequest with await and async can be.

Source code for this sample: http://www.billreiss.com/wp-content/uploads/2013/01/TestJsonWebAsync.zip