HttpClient is Here!


HttpClient is a modern HTTP client for .NET. It provides a flexible and extensible API for accessing all things exposed through HTTP. HttpClient has been available for a while as part of WCF Web API preview 6 but is now shipping as part of ASP.NET Web API and directly in .NET 4.5. You can also download it using NuGet – we just use the following three NuGet packages in this blog:

  1. System.Net.Http: The main NuGet package providing the basic HttpClient and related classes
  2. System.Net.Http.Formatting: Adds support for serialization, deserialization as well as for many additional features building on top of System.Net.Http
  3. System.Json: Adds support for JsonVaue which is a mechanism for reading and manipulating JSON documents

In case you haven’t played with HttpClient before, here is a quick overview of how it works:

HttpClient is the main class for sending and receiving HttpRequestMessages and HttpResponseMessages. If you are used to using WebClient or HttpWebRequest then it is worth noting that HttpClient differs in some interesting ways – here’s how to think about an HttpClient:

  1. An HttpClient instance is the place to configure extensions, set default headers, cancel outstanding requests and more.
  2. You can issue as many requests as you like through a single HttpClient instance.
  3. HttpClients are not tied to particular HTTP server or host; you can submit any HTTP request using the same HttpClient instance.
  4. You can derive from HttpClient to create specialized clients for particular sites or patterns
  5. HttpClient uses the new Task-oriented pattern for handling asynchronous requests making it dramatically easier to manage and coordinate multiple outstanding requests.

Kicking the Tires

We will be diving into lots of details in the near-future around features, extensibility, serialization, and more but let’s first kick the tires by sending a request to the World Bank Data Web API. Please see List of ASP.NET Web API and HttpClient Samples for the complete sample solution. The service provides all kinds of information about countries around the world. It exposes the data both as XML and as JSON but in this sample we download it as JSON and use JsonValue to traverse the request for interesting information without having to create a CLR type on the client side:

   1: /// <summary>
   2: /// Sample download list of countries from the World Bank Data sources at http://data.worldbank.org/
   3: /// </summary>
   4: class Program
   5: {
   6:     static string _address = "http://api.worldbank.org/countries?format=json";
   7:  
   8:     static void Main(string[] args)
   9:     {
  10:         // Create an HttpClient instance
  11:         HttpClient client = new HttpClient();
  12:  
  13:         // Send a request asynchronously continue when complete
  14:         client.GetAsync(_address).ContinueWith(
  15:             (requestTask) =>
  16:             {
  17:                 // Get HTTP response from completed task.
  18:                 HttpResponseMessage response = requestTask.Result;
  19:  
  20:                 // Check that response was successful or throw exception
  21:                 response.EnsureSuccessStatusCode();
  22:  
  23:                 // Read response asynchronously as JsonValue and write out top facts for each country
  24:                 response.Content.ReadAsAsync<JsonArray>().ContinueWith(
  25:                     (readTask) =>
  26:                     {
  27:                         Console.WriteLine("First 50 countries listed by The World Bank...");
  28:                         foreach (var country in readTask.Result[1])
  29:                         {
  30:                             Console.WriteLine("   {0}, Country Code: {1}, Capital: {2}, Latitude: {3}, Longitude: {4}",
  31:                                 country.Value["name"],
  32:                                 country.Value["iso2Code"],
  33:                                 country.Value["capitalCity"],
  34:                                 country.Value["latitude"],
  35:                                 country.Value["longitude"]);
  36:                         }
  37:                     });
  38:             });
  39:  
  40:         Console.WriteLine("Hit ENTER to exit...");
  41:         Console.ReadLine();
  42:     }
  43: }

The HttpResponseMessage contains information about the response including the status code, headers, and any entity body. The entity body is encapsulated in HttpContent which captures content headers such as Content-Type, Content-Encoding, etc. as well as the actual content. The content can be read using any number of ReadAs* methods depending on how you would like to consume the data. In the sample above, we read the content as JsonArray so that we can navigate the data and get the information we want.

Trying out .Net 4.5

An exciting feature in .NET 4.5 is the language support for asynchronous programming that makes programming Tasks even easier. Using the new async and await language keywords, we can replace the “ContinueWith” pattern in the sample above and get something that looks very clean like this:

   1: static string _address = "http://api.worldbank.org/countries?format=json";
   2:  
   3: static async void Run()
   4: {
   5:     // Create an HttpClient instance
   6:     HttpClient client = new HttpClient();
   7:  
   8:     // Send a request asynchronously continue when complete
   9:     HttpResponseMessage response = await client.GetAsync(_address);
  10:  
  11:     // Check that response was successful or throw exception
  12:     response.EnsureSuccessStatusCode();
  13:  
  14:     // Read response asynchronously as JsonValue and write out top facts for each country
  15:     JsonArray content = await response.Content.ReadAsAsync<JsonArray>();
  16:  
  17:     Console.WriteLine("First 50 countries listed by The World Bank...");
  18:     foreach (var country in content[1])
  19:     {
  20:         Console.WriteLine("   {0}, Country Code: {1}, Capital: {2}, Latitude: {3}, Longitude: {4}",
  21:             country.Value["name"],
  22:             country.Value["iso2Code"],
  23:             country.Value["capitalCity"],
  24:             country.Value["latitude"],
  25:             country.Value["longitude"]);
  26:     }
  27: }
  28:  
  29: static void Main(string[] args)
  30: {
  31:     Run();
  32:     Console.WriteLine("Hit ENTER to exit...");
  33:     Console.ReadLine();
  34: }

Have fun!

Henrik

del.icio.us Tags: ,,,,,
Comments (37)

  1. Tugberk says:

    Hi,

    After checking your samples, I saw that you didn't perform the dispose action on HttpClient instance. I have used all instances of HttpClient with using statement on my app and I thought that it is the right way since HttpClient  implements the IDisposable interface. Am I on the right path?

  2. Henrik Frystyk Nielsen says:

    In general that is correct although you have to be careful with "using" and async as they dont' really mix in .Net 4, In .Net 4.5 you can use "await" inside a "using" statement.

    Btw, you can reuse the same HttpClient as many times are you like so typically you won't create/dispose them all the time.

  3. J. Daniel Smith says:

    See my recent question at stackoverflow.com/…/using-httpclient-how-can-i-save-a-xdocument-directly-to-the-request-stream about writing a XDocument directly to the request stream.

  4. Henrik Frystyk Nielsen says:

    Dan, I just posted a blog called "Push and Pull Streams using HttpClient" on this topic, please see blogs.msdn.com/…/push-and-pull-streams-using-httpclient.aspx

  5. Mikael Koskinen says:

    Hi,

    I was wondering about what platforms are supported by the HttpClient? For example, does it work with Silverlight / WP7?

  6. Henrik Frystyk Nielsen says:

    Mikael, at the moment it works on .Net desktop and Windows 8. We are looking at making it available elsewhere but i don't havy anything concrete yet.

  7. John Bubriski says:

    So all the Synchronous code was removed from the HttpClient class?

  8. John Bubriski says:

    It looks like we can do this if we want things the old synchronous way 😛

    public T GetDataFromService<T>()

    {

       using (var httpClient = new HttpClient())

       {

           var response = httpClient.GetAsync(_endpoint).Result;

           return JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result);

       }

    }

  9. ReadAsAsync not in HttpContent says:

    Hi, I was trying to use your code in my machine but it seems that I don't have the version you mentioned. I have VS11 Developer preview (v 11.0.40825.2 PREREL). I've referenced System.Net.Http, but my HttpContent class doesn't have a method ReadAsAsync (just ReadAsByteArray and ReadAsString). Is this something will be available to the general public in the future, maybe next VS release ?

  10. Henrik Frystyk Nielsen says:

    ReadAsAsync can be found in the System.Net.Http.Formatting NuGet package.

  11. Fred says:

    can you post an example for using post with HttpClient. I have an object called merchant and I would like to post it using httpclient to my asp .net web api service.

  12. Bob Briggs says:

    I tried using the example snippet you posted here and the funny thing was that the current version I pulled down with NuGet doesn't have a single argument call for GetAsync(string).  Is it true that this is a breaking change with your example?

  13. Bob Briggs says:

    Never mind!  My goof!  I was using PostAsync().  It's the details that getcha!

  14. quachnguyen says:

    Hello Henrik F Nielsen,

    Small question about HttpClient library, Is this library supported proxy?

  15. I_HATE_BETA_CODE says:

    The PostAsync() method seems to be broken. You constantly run into a message like this:

    "The exception message is 'The service operation 'InsertAddressType'

    expected a value assignable to type 'AddressType' for input parameter

    'AddressType' but received a value of type 'HttpRequestMessage`1' "

    Some examples on how to put/post complex types from HttpClient would be very nice.

  16. SCV says:

    Related to that,

    foreach (var course in readTask.Result[1]) this works at compile time,

    but foreach (var course in readTask.Result), doesn't work at compile time and gives following error:

    "System.Threading.Tasks.Task" does not contain definition for 'Result'

    Trying to understand what's the difference and why it's not working?

    Because my result it:{[{"id":0,"name":".net"},{"id":1,"name":"oData"},{"id":2,"name":"WebApi"}]}

    How do i retrieve id and name?

  17. SCV says:

    looks like my previous post didn't go through. i'll type again:

    response.Content.ReadAsAsync<JsonArray>() returns System.Threading.Tasks.Task<JsonArray>, but when i check properties of System.Threading.Tasks.Task<>, didn't find "Result" as property.

    Then how "foreach (var course in readTask.Result[1])" works?

    if we put foreach (var course in readTask.Result) that doesn't work.

    Please explain!

  18. SCV says:

    FYI: i'm using [ ] instead <>, may be that's the reason my posts are not going through

    looks like my previous post didn't go through. i'll type again:

    response.Content.ReadAsAsync[JsonArray]() returns System.Threading.Tasks.Task[JsonArray[, but when i check properties of System.Threading.Tasks.Task[], didn't find "Result" as property.

    Then how "foreach (var course in readTask.Result[1])" works?

    if we put foreach (var course in readTask.Result) that doesn't work.

    Please explain!

  19. CitriusJohn says:

    Is there an update to these samples when moving to RC?  It seems line 24 in the first example is no longer correct.  I run this sample as-is and I get the following error:

    System.AggregateException was unhandled by user code

     HResult=-2146233088

     Message=One or more errors occurred.

     Source=mscorlib

     StackTrace:

          at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)

          at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)

          at System.Threading.Tasks.Task`1.get_Result()

          at WorldBankSample.Program.<Main>b__1(Task`1 readTask) in c:UsersJohnDocumentsVisual Studio 2012ProjectsIntroduction to HttpClientC#WorldBankSampleProgram.cs:line 39

          at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()

          at System.Threading.Tasks.Task.Execute()

     InnerException: System.InvalidOperationException

          HResult=-2146233079

          Message=Cannot create and populate list type System.Json.JsonArray.

          Source=Newtonsoft.Json

          StackTrace:

               at Newtonsoft.Json.Utilities.CollectionUtils.CreateList(Type listType, Boolean& isReadOnlyOrFixedSize)

               at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)

               at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)

               at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)

               at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)

               at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)

               at System.Net.Http.Formatting.JsonMediaTypeFormatter.<>c__DisplayClass8.<ReadFromStreamAsync>b__6()

               at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)

          InnerException:

  20. anil says:

    What is OAUTH and How can i use in my ASP.NET website

    pls tell me one simple example

    send source code my mail id:mandla.anilbabu@gmail.com

    pls help me……….

  21. dc.groups says:

    any way to make the requests go through a proxy?

  22. li says:

    hi, a bug for Task<HttpResponseMessage>.Wait()

    throw System.ObjectDisposedException

    can't catch

    Dead loop

    cpu 100%

  23. Dear Henrik,

    can you show an example on how to upload a file using http client with form data

    Thanks,

    Neon

  24. Peter says:

    Can we please have some parameter samples?

  25. Robb Sadler says:

    The code above is not quite right anymore, but it has been updated to use the latest NewtonSoft JArray at aspnet.codeplex.com/…/15dfe7e0759f

    The corrected code is:

    using System;

    using System.Net.Http;

    using Newtonsoft.Json.Linq;

    namespace WorldBankSample

    {

       /// <summary>

       /// Sample download list of countries from the World Bank Data sources at http://data.worldbank.org/

       /// </summary>

       class Program

       {

           private static string _address = "api.worldbank.org/countries

           private static async void RunClient()

           {

               // Create an HttpClient instance

               HttpClient client = new HttpClient();

               // Send a request asynchronously and continue when complete

               HttpResponseMessage response = await client.GetAsync(_address);

               // Check that response was successful or throw exception

               response.EnsureSuccessStatusCode();

               // Read response asynchronously as JToken and write out top facts for each country

               JArray content = await response.Content.ReadAsAsync<JArray>();

               Console.WriteLine("First 50 countries listed by The World Bank…");

               foreach (var country in content[1])

               {

                   Console.WriteLine("   {0}, Country Code: {1}, Capital: {2}, Latitude: {3}, Longitude: {4}",

                       country.Value<string>("name"),

                       country.Value<string>("iso2Code"),

                       country.Value<string>("capitalCity"),

                       country.Value<string>("latitude"),

                       country.Value<string>("longitude"));

               }

           }

           static void Main(string[] args)

           {

               RunClient();

               Console.WriteLine("Hit ENTER to exit…");

               Console.ReadLine();

           }

       }

    }

    HTH…

  26. Brian11 says:

    It only returns a AggregateException with a inner taskcanceledException. How can you get a webexception? ie, the uri is not reachable or the opertation has timed out?

  27. Elliot says:

    Hi,

    I am not sure why ReadAsAsync<JArray>() is asynchronous. What IO is being done here? I can't see anything happening on wireshark for this call; it's all done earlier.

  28. Jerry Boggess says:

    I plan to use the 4.5 HttpClient in a 24/7 Windows Service.  The service will only recycle when the server reboots for monthly patches.  I instantiate the client when the Windows service starts and dispose of it when the service shuts down. The client may make one or 1000s of calls per day.   I have read on some sites the .Net HtpClient has a memory leak and needs to be disposed somewhat frequently.   Can you provide if these other sites are correct or bogus?  Thank you.

  29. Henrik Frystyk Nielsen says:

    Jerry,

    I don't know of any memory leaks — it should be fine to use it this way.

    Henrik

  30. peter says:

    Hi,

    I am using vs 2013. the system.json cannot be found, so that JsonArray cannot be used. So which namespace contains JsonArray ?

    Thank you.

  31. kallol says:

    Thx for your post. Quick question:

    Is it possible to send an HttpMessageHandler object to Httpclient without creating a fresh instance of the httpclient. Right now, httpclient accepts the handler in its constructor. What do I do, if I need to pass the handler  after the httpclient is creating.

    What I am trying to achieve is get use a single httpclient object to make 5000 requests and each request on a different proxy – which is where I need a different handler object.

  32. Dean Severson says:

    Thanks Henrik, this got me what I needed. Good post.

  33. Brooks Slocum says:

    how would you go about putting the Json results into an array of objects? And then put a specific one into a textbox for example.

  34. Anonymous says:

    I'm trying to use HTTP Client too, but fell into a HTTPrequestexception, is there anyone who can help?

    Here are my codes:

           private async void btn_Click(object sender, EventArgs e)

           {

               string authority = "login.windows.net/activedirectoryAD.onmicrosoft.com";

               string resourceURI = "activedirectoryad.onmicrosoft.com/WebAPI";

               string clientID = "6c9677d8-4d11-40a2-a96c-7b998d080d15";

               Uri returnURI = new Uri("http://webapiclient&quot;);

               //authentication and get access token

               AuthenticationContext authContext = new AuthenticationContext(authority);

               AuthenticationResult authResult =

               authContext.AcquireToken(resourceURI, clientID, returnURI);

               string authHeader = authResult.CreateAuthorizationHeader();

               System.Net.ServicePointManager.ServerCertificateValidationCallback =

               ((s, c, c2, se) => true);

               HttpClient client = new HttpClient();

               HttpRequestMessage request =

                   new HttpRequestMessage(HttpMethod.Get, "https://localhost:44300/api/tasks&quot;);

               request.Headers.TryAddWithoutValidation("Authorization", authHeader);

               var response = await client.SendAsync(request);

               //throw if not success code

               //response.EnsureSuccessStatusCode();

               //string responseString = await response.Content.ReadAsAsync();

               string responseString = await response.Content.ReadAsStringAsync();

               MessageBox.Show(responseString);

           }

  35. Anonymous says:

    According to Nuget System.Json has been deprecated…

  36. chepa says:

    Does the HttpClient obey the ServicePointManager settings like Expect100 and UseNaggleAlgorithm ?