The HttpClient
class is used in modern .NET applications to make HTTP requests. It was introduced in .NET 4.5 as a replacement for HttpWebRequest
and WebClient
.
This post collects some usage guidelines for HttpClient
that may not be obvious.
Per MSDN, “HttpClient
is intended to be instantiated once and re-used throughout the life of an application.” The rationale is mentioned in that MSDN article and described in detail in this blog post.
All HttpClient
methods for making HTTP requests are thread-safe, so as long as you don’t change any of its properties (BaseAddress
, DefaultRequestHeaders
, Timeout
, etc.) after you start making requests, reusing the same instance on any thread will be fine.
The simplest way to reuse HttpClient
instances is to create a static readonly field in the class that needs to make HTTP requests. Of course, that still results in one instance per class, so consider allowing the HttpClient
instance to be specified via constructor or method parameter.
HttpClient
is disposable, but there’s no harm in never disposing it if it is used for the life of the application.
Example: The HttpClientService
class of Facility.Core
accepts an HttpClient
instance in the constructor, but if it isn’t specified, it uses a static readonly instance that is never disposed.
The SendAsync
method is the most flexible way to send an HTTP request with HttpClient
. It accepts an HttpRequestMessage
and returns an HttpResponseMessage
. Note that both classes are disposable; be sure to dispose of instances of both classes when you are finished with them.
Example: FacilityCSharp has tests that run a number of HTTP requests in a row that POST
a JSON body and process a JSON result. After a few dozen requests, HttpClient
would asynchronously deadlock. I never got to the bottom of it, but I did find a solution: dispose the HTTP requests and responses.
When an HTTP request times out, an OperationCanceledException
exception is thrown, not a TimeoutException
.
The default timeout is 100 seconds. If you are using a single HttpClient instance (see above), you’ll want to make sure that HttpClient.Timeout
is set as high as any request might need.
To use a shorter timeout for certain requests, use CancellationTokenSource.CancelAfter
and send in the corresponding cancellation token. If you already have a cancellation token, use CancellationTokenSource.CreateLinkedTokenSource
and call CancelAfter
on that. If you need to distinguish between the two types of cancellation, check if the original cancellation token is cancelled.
Example: This StackOverflow answer demonstrates combining a cancellation token and a timeout.
I haven’t attempted to reproduce this behavior, but apparently using a singleton HttpClient
doesn’t respect DNS changes. For more information, read this article and this article.
Apparently you can use ServicePoint.ConnectionLeaseTimeout
to work around this problem (see linked articles).
Aside: FacilityCSharp does not address this problem, but if you’re a Facility user and experience it, please file an issue and let us know!
Posted by Ed Ball on March 30, 2017