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.
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
1 app.GetWithWinJSxhr = <span>function</span> () <br /> <span>var</span> xhrDiv = document.getElementById(<span>"xhrReport"</span>);<br /> xhrDiv.style.color = <span>"#000000"</span>;<br /> xhrDiv.innerText = <span>"Running..."</span>;<br /> WinJS.xhr( url: <span>"http://www.microsoft.com"</span> ).done(<br /> <span>function</span> complete(result) <br /> xhrDiv.innerText += <span>"nDownloaded pagenn"</span> + result.responseText;<br /> xhrDiv.style.backgroundColor = <span>"#00FF00"</span>;<br /> ,<br /> <span>function</span> error(result) <br /> xhrDiv.innerText += <span>"nGot error: "</span> + result.statusText;<br /> xhrDiv.style.backgroundColor = <span>"#FF0000"</span>;<br /> ,<br /> <span>function</span> progress(progress) <br /> xhrDiv.innerText += <span>"nReady state is "</span> + progress.readyState;<br /> xhrDiv.style.backgroundColor = <span>"#0000FF"</span>;<br /> <br /> );<br />}<br />
1 app.GetWithHttpClient = <span>function</span> () <br /> <span>var</span> xhrDiv = document.getElementById(<span>"xhrReport"</span>);<br /> xhrDiv.style.color = <span>"#000000"</span>;<br /> xhrDiv.innerText = <span>"Running..."</span>;<br /> <span>var</span> hc = <span>new</span> Windows.Web.Http.HttpClient(); <span>// Change #1</span><br /> <span>var</span> uri = <span>new</span> Windows.Foundation.Uri(<span>"http://www.microsoft.com"</span>); <span>// Change #1</span><br /> hc.getStringAsync(uri).done( <span>// Change #1</span><br /> <span>function</span> complete(result) <br /> xhrDiv.innerText += <span>"nDownloaded pagen"</span> + result; <span>// Change #2</span><br /> xhrDiv.style.backgroundColor = <span>"#00FF00"</span>;<br /> ,<br /> <span>function</span> error(result) <br /> <span>var</span> webError = Windows.Web.WebError.getStatus(result.number); <span>// Change #3</span><br /> xhrDiv.innerText += <span>"nError "</span> + webError + <span>":"</span> + result.message; <span>// Change #3</span><br /> xhrDiv.style.backgroundColor = <span>"#FF0000"</span>;<br /> ,<br /> <span>function</span> progress(progress) <br /> xhrDiv.innerText += <span>"nReady state is "</span> + progress.stage; <span>// Change #4</span><br /> xhrDiv.style.backgroundColor = <span>"#0000FF"</span>;<br /> <br /> );<br />}<br />
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:
1 <span>new</span> Windows.Foundation.Uri(<span>string</span>-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
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
Open method was called successfully
|Connecting to server|
|Waiting for response|
|Headers have been received|
|Content is being received||
|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
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.
Reason #2: Access to cookies
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’:
1 <span>var</span> bpf = <span>new</span> HttpBaseProtocolFilter();<br /><span>var</span> cookieManager = bpf.CookieManager;<br /><span>var</span> cookie = <span>new</span> HttpCookie(“myCookieName”, “.example.com”, “/”);<br />cookie.Value = “myValue”;<br />cookieManager.SetCookie(cookie);<br /><br /><span>// Use this base protocol file with an HttpClient.</span><br />Var httpClient = <span>new</span> HttpClient(bpf);<br />
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.
Step 3: Right-click the solution and click Add>Existing Project to add the downloaded HttpClient sample’s HttpFilters project.
The code changes are below. After the HttpClient object is created with the new filter pipeline, the rest of the code is unchanged.
1 <span>var</span> bpf = <span>new</span> Windows.Web.Http.Filters.HttpBaseProtocolFilter();<br /><span>var</span> retryFilter = <span>new</span> HttpFilters.HttpRetryFilter(bpf);<br /><span>var</span> hc = <span>new</span> Windows.Web.Http.HttpClient(retryFilter);<br />
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.
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
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
Originally from –