Are you a JavaScript app developer who connects to a web service? If so, there's a new HTTP API in Windows 8.1 that improves on the abilities of the WinJS.xhr and XMLHttpRequest functions. With the Windows.Web.HttpClient API you have access to cookies, control over caching, and can set and get headers using strong types. There’s also a powerful system for modularizing your network code, giving it access to the HTTP processing pipeline. If you're a C++ or .NET developer, it's available for your app, too. (And it’s not just for Windows Store apps: it works on the desktop, as well.) If you want to just jump into the documentation, there’s reference material that includes quick code snippets and a full sample.
In this blog post you'll see an overview of HttpClient programming, get a walkthrough of converting a JavaScript program to use the new API, and read about the advantages of the new API. Let's get started with an overview of the new classes!
An overview of HttpClient programming
The new HTTP classes are all in the Windows.Web.Http namespace and the two sub-namespaces Headers and Filters. These namespaces contain a family of classes that work together to give you an easy-to-use but powerful HTTP API, as shown below:
How your code, the HttpClient API, and the REST or Web Service fit together
In the diagram, light green is your code. On the left side is the business logic for your app. In the center are your filters: modular code that you can put into the middle of the HTTP processing pipeline. By moving code into filters, your business logic can focus on your app and not on low-level networking details. You can build your own filters, reuse them from our HttpClient and Web Authentication Broker samples, or find them on the internet.
You'll start by making a call to one of the HttpClient object's methods. A commonly used method is getStringAsync(Windows.Foundation.Uri); this method, given a Uri, returns the content as a string (or triggers an error). The most general method on the HttpClient class is sendRequestAsync (HttpRequestMessage); it returns an HttpResponseMessage with the server headers, status code, the content, and more.
No matter what method you call, the HttpClient object packages up your request into an HttpRequestMessage (in the case of sendRequestAsync, you provide one of these). The HttpRequestMessage object is then passed through each filter in the filter pipeline, if they exist, finally passing through the Windows.Web.Http.Filters.HttpBaseProtocolFilter, which actually sends your HTTP message out. The base protocol filter also contains a set of properties that let you influence how your HTTP requests and responses are handled. The actual filter pipeline is created by you, and passed into the HttpClient constructor. If you don't specify a filter pipeline, a default pipeline (consisting of just a new HttpBaseProtocolFilter) is created for you.
The response from the web service is then packaged up by the HttpBaseProtocolFilter and passed back up the filter pipeline. Each of the filters has full control over the response that the filter returns: it can return the incoming response (possibly modifying it first), can synthesize a new response, or can retry the original request or a modification of the original request. The final response is then returned to you. The 'get' convenience routines like getStringAsync extract the content and return it to you as a string, input stream, or buffer.
Note that the HttpClient classes can always trigger an error! Your code needs to be able to handle network errors ranging from simple network connectivity issues to DNS failures, server errors, and SSL errors.
Once you have your response, you can use it just like you do today.
Converting JavaScript WinJS.xhr code to use HttpClient
Now that you've seen the overall HttpClient API, let's look at how to convert your existing WinJS.xhr calls into HttpClient calls. We'll do this by converting code inspired by the Windows 8 QuickStart: Downloading a file with WinJS.xhr. You’ll see that the changes are short and simple:
Original WinJS.xhr code
app.GetWithWinJSxhr = function () {
var xhrDiv = document.getElementById("xhrReport");
xhrDiv.style.color = "#000000";
xhrDiv.innerText = "Running...";
WinJS.xhr({ url: "http://www.microsoft.com" }).done(
function complete(result) {
xhrDiv.innerText += "\nDownloaded page\n\n" + result.responseText;
xhrDiv.style.backgroundColor = "#00FF00";
},
function error(result) {
xhrDiv.innerText += "\nGot error: " + result.statusText;
xhrDiv.style.backgroundColor = "#FF0000";
},
function progress(progress) {
xhrDiv.innerText += "\nReady state is " + progress.readyState;
xhrDiv.style.backgroundColor = "#0000FF";
}
);
}
HttpClient code
app.GetWithHttpClient = function () {
var xhrDiv = document.getElementById("xhrReport");
xhrDiv.style.color = "#000000";
xhrDiv.innerText = "Running...";
var hc = new Windows.Web.Http.HttpClient();// Change #1
var uri = new Windows.Foundation.Uri("http://www.microsoft.com");// Change #1
hc.getStringAsync(uri).done( // Change #1
function complete(result) {
xhrDiv.innerText += "\nDownloaded page\n" + result; // Change #2
xhrDiv.style.backgroundColor = "#00FF00";
},
function error(result) {
var webError = Windows.Web.WebError.getStatus(result.number);// Change #3
xhrDiv.innerText += "\nError "+ webError + ":" + result.message;// Change #3
xhrDiv.style.backgroundColor = "#FF0000";
},
function progress(progress) {
xhrDiv.innerText += "\nReady state is " + progress.stage; // Change #4
xhrDiv.style.backgroundColor = "#0000FF";
}
);
}
Change #1: objects and parameter type
The first set of changes is that we replace the call to WinJS.xhr with the creation of a new HttpClient object and a call to getStringAsync. You can make as many (or few) HttpClient objects that your app needs. Because each HttpClient can be individually configured (e.g., for cache control), it often makes sense to make one HttpClient per general configuration. For example, an app that starts out only reading data from cache and then switches to reading from the internet might have two HttpClient objects, one for “reading from cache” and one for “reading fresh content.”
Secondly, the HttpClient always takes in Uri objects, not strings. You can easily make a Uri from a string; just construct one with:
new Windows.Foundation.Uri(string-parameter)
Change #2: success response
You'll notice that the parameter passed to you in the complete function is the downloaded content string instead of the XMLHttpRequest that WinJS.xhr will pass you. If you do need precise information about the server response, call the getAsync() method instead of getStringAsync() as it provides an HttpResponseMessage object. That object includes full details on the original server response. You can also get a buffer or inputStream by calling getBufferAsync() or getInputStreamAsync(), respectively.
Change #3: the error callback
The error function for HttpClient is a standard WinRTError object that mimics the JavaScript ‘Error’ object. Useful fields include description, message, and number. The error.number is a windows HRESULT value which you can convert to a WebErrorStatus by calling:
Windows.Web.WebError.getStatus(hresult)
Change #4: the progress callback
The progress function for HttpClient gives you an HttpProgress object. Like the WinJS.xhr() progress calls, you can find out the overall progress of your HTTP call. The key difference is that the progress value is called ‘stage’ with HttpClient and ‘readyState’ in the WinJS.xhr progress calls. The values are different, too: HttpClient gives you a more fine-grained insight into the exact HTTP processing stage. This is listed in the following table.
Table: WinJS.xhr versus HttpProgress states
WinJS.xhr readyState Numeric Value | WinJS.xhr.readyState Meaning | HttpProgress.stage Numeric Value | HttpProgress.stage Meaning |
| Open method was called successfully | ||
| Detecting proxy | ||
| Resolving name | ||
| Connecting to server | ||
| Negotiating SSL | ||
| Sending headers | ||
| Sending content | ||
| Waiting for response | ||
| Receiving headers | ||
| Headers have been received | ||
| Content is being received |
| Receiving content |
| All content has been received |
With this last change we’re done. Our code now uses the HttpClient API instead of WinJS.xhr.
Advantages of the HttpClient family of classes
Now let’s look at some of the advantages of the HttpClient APIs from the 2013 //Build/ talk “Five great reasons to use the new HttpClient API.” Only four of the reasons apply to JavaScript, so we’ll just discuss four reasons here.
Reason #1: Strongly typed headers
The WinJS.xhr function lets you set an HTTP header for a request. But the headers are specified just as strings: you need to be quite knowledgeable in the exact data format, and errors are hard to catch. The HttpClient API lets you specify HTTP header values using strong types that reduce errors and handle the correct header formatting for you.
For example, suppose you want to read the last-modified date sent by the server. With strongly typed headers, you just examine the response.content.headers.lastModified value as a JavaScript date object. You don’t have to loop through the different headers, doing string compares (case insensitive, per RFC 2616!) and then parsing the date field yourself. Instead the value is simply handed to you. If you need the original headers (as strings), they’re all available to you.
Reason #2: Access to cookies
The existing JavaScript HTTP APIs handle cookies automatically: cookies sent to your app by servers are parsed and stored as needed for your app, and are automatically formatted and sent back to the server as needed. But previously your app couldn’t participate in this code: you couldn’t set or delete or list those cookies. HttpClient includes easy-to-use APIs that let you access the cookie container. As you might expect, Windows Store apps can only access their own cookies; you aren’t allowed to examine, set, or delete cookies from other apps.
All access to cookies is from the CookieManager object that’s part of the HttpBaseProtocolFilter. The CookieManager has three methods: deleteCookie, getCookies, and setCookie. As an example, here’s how to set a cookie called ‘myCookieName’ that will be sent when you send a request to any path in any sub-domain of ‘example.com’:
var bpf = new HttpBaseProtocolFilter();
var cookieManager = bpf.CookieManager;
var cookie = new HttpCookie(“myCookieName”, “.example.com”, “/”);
cookie.Value = “myValue”;
cookieManager.SetCookie(cookie);
// Use this base protocol file with an HttpClient.
Var httpClient = new HttpClient(bpf);
In the sample, we first get a CookieManager from an HttpBaseProtocolFilter. Then we create a cookie, set its value, and then set the cookie into the CookieManager.
Reason #3: Control over caching
Normally you don’t need to worry about caching. The server generally sets the right kind of headers on the HTTP responses, and the stack returns either cached or non-cached data as appropriate. But sometimes you need more control. The Windows.Web.Http classes let you control both how data is read from the network cache and when the network cache is updated with server responses. Caching is controlled with the cacheControl sub-object in the HttpBaseProtocolFilter. Note that each instance of an HttpClient generally has its own HttpBaseProtocolFilter, each of which is individually controlled. Changing a setting for one won’t change the setting for another.
The Windows.Web.Http.Filter.HttpCacheReadBehavior enumeration has three settings for reading from the cache:
- default means to work like a web browser works: if the resource is in the network cache, and it’s not expired (based on expiration data originally provided by the server), the cached resource is returned; otherwise, the HttpBaseProtocolFilter calls out to the web service to get the resource.
- mostRecent automatically does an if-modified-since back and forth with the server. If the resource is in the cache, we’ll automatically ask the web server for the resource, but with an if-modified-since header that’s initialized from the cached resource information. If the server returns a new version of the resource, that new version is returned; otherwise the cached value is returned. If the resource wasn’t in the cache, it’s retrieved from the server.
This is a great option when you need the freshest possible data and can afford the additional delays from the extra network round-trips. - onlyFromCache means that only data from the cache is returned; if the requested network data isn’t in the cache, the operation completes with an error (“The system cannot find the file specified”) . To help your app start faster: when the app starts, you can require all resources be read from the cache, which is much faster than reading from the network. After the app starts, you can re-get the data, only this time actually allowing network access.
If you combine this with the ContentPrefetcher in Windows 8.1, the user can get the best of both worlds: the app launch speed of showing cached content and the freshness of seeing new-to-them content right at startup. The ContentPrefetcher class provides a mechanism for specifying resources that Windows should try to download in advance of your app being launched by the user. For more info about the ContentPrefetcher, see Matt Merry and Suhail Khalid’s 2013 //build/ talk “Building Great Service Connected Apps.”
Reason #4: Place your code modules into the processing pipeline for cleaner, more modular code
It’s great when your business logic can just make simple HTTP requests to web services. But at the same time, your app needs to handle a variety of conditions: your code needs to handle authentication, work correctly for network retries, handle metered networks and roaming, and more. The HttpClient API lets you create filters -- chunks of modular code written to a common interface -- to handle these common cases, and lets you place them into the HTTP processing pipeline. Each filter sees the requests going out and the responses coming back, and can modify the requests and responses as needed.
Let’s demonstrate this by adding the HTTP sample 503 retry filter into the demo code. To do this, we need to add the HttpFilters project from the HttpClient sample as a new project in our solution, add the HttpFilters as a reference in our JavaScript project, and create a filter pipeline that uses the HttpRetryFilter and the HttpBaseProtocolFilter. You can get the HttpClient sample from the HttpClient sample (Windows 8.1). The 503 Retry filter, on getting a 503 error from a server, automatically retries the request.
Step 1: Download the HttpClient sample (the C++, JavaScript) into a new directory. Remember where you downloaded it to!
Step 2: Open your JavaScript app project file.
Step 3: Right-click the solution and click Add>Existing Project to add the downloaded HttpClient sample’s HttpFilters project.
Step 4: In the JavaScript code project, right-click References>Add Reference. In the resulting Reference Manager dialog box, pick the HttpFilters reference from the solution/projects tab
Step 5: Create a filter pipeline in your JavaScript code and pass the filter pipeline into your HttpClient object. Filter pipelines are generally constructed from the HttpBaseProtocolFilter side and work their way to the HttpClient object. Each filter commonly takes in the pipeline-so-far in its constructor.
In the JavaScript code, you’ll first create the HttpBaseProtocolFilter. This filter doesn’t take in any parameters. Then you’ll construct the HttpFilters.HttpRetryFilter. The constructor takes in a single parameter: an IHttpFilter. Pass in the HttpBaseProtocolFilter that you just constructed. Lastly, the new HttpClient constructor can also take in an IHttpFilter object; pass in the HttpRetryFilter that you just made. The result is an HttpClient with a filter pipeline consisting of two filters: the HttpRetryFilter and the HttpBaseProtocolFilter.
The code changes are below. After the HttpClient object is created with the new filter pipeline, the rest of the code is unchanged.
var bpf = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
var retryFilter = new HttpFilters.HttpRetryFilter(bpf);
var hc = new Windows.Web.Http.HttpClient(retryFilter);
If you need to debug the C++ filter code, set your project for “Native with Script” debugging. This is set in project properties, in the “Debugger Type” field of the debugger tab. If you need to just debug the native (C++) code and not JavaScript, you can set Debugger Type to Native Only.
That’s all you need to do for your code to handle server 503 retries correctly. And if our retry filter doesn’t fully meet your needs, you have the source code ready to be updated to your specifications.
In Closing
The Windows.Web.Http classes have powerful features including strongly typed headers, access to cookies, useful control over caching, and filters that let you inject your code into the HTTP processing pipeline. These classes let you connect your app to web services with a minimum of code and a maximum of power and flexibility.
-Peter Smith, Senior Program Manager
More info
Can’t get enough? Check out these great links:
- The documentation is at http://msdn.microsoft.com/library/windows/apps/dn298639
- The HttpClient sample is at http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664
- The Web Authentication Broker sample includes filters for OAuth and OAuth 2.0. Drop them into your filter pipeline and with a bit of configuration you’ll be able to access popular websites with ease! The sample is at http://code.msdn.microsoft.com/windowsapps/Web-Authentication-d0485122
- Check out the forums: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/home?category=windowsapps
- There’s a Build talk about HttpClient: http://channel9.msdn.com/Events/Build/2013/4-092
- There’s another Build talk about other networking APIs including the ContentPrefetcher feature at http://channel9.msdn.com/Events/Build/2013/3-090