There is a similar SO question here which talks about the performance of HttpClient objects and recommends to use one HttpClient instance per application. My project requires me to send multiple webservice requests to the same URI but each with a different set of headers. Should I create a new HttpClient instance for every request, knowing the fact that DefaultRequestHeaders will be same for all requests if I use a static instance.
You can:
Set default headers on your global instance
Create multiple global (logical) instances with different default configurations
Set (additional) headers per request
Create new HttpClient using IHttpClientFactory
Docs: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.1
Global Default Headers
Here you create one client instance and add headers that will be applied to all requests.
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Content-Type", contentTypeValue);
Multiple Preconfigured Instances
In this dotnet core 2.1 example, we register a preconfigured named instance:
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
// Github API versioning
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
// Github requires a user-agent
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
});
Later we'll inject IHttpClientFactory, and get this pre configured client like so:
var client = _clientFactory.CreateClient("github");
An alternative to named client are "typed clients": https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0#typed-clients. Both approaches will keep the basic configuration at one place.
Headers per Request
If your headers belong to a single request only, simple set them per request.
var client = new HttpClient();
var request = new HttpRequestMessage();
request.Headers.Add("Content-Type", "text/plain");
var response = await client.SendAsync(request);
Using this approach you can use a shared HttpClient instance.
IHttpClientFactory
If you want a new "clean" HttpClient instance, the recommended approach for asp.net core is to inject IHttpClientFactory and use _clientFactory.CreateClient().
public class MyService {
public MyService (IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task DoSomething()
{
var client = _clientFactory.CreateClient();
// do request
}
}
Related
Given that the .net HttpClient has been designed with reuse in mind and is intended to be long lived and memory leaks have been reported in short lived instances. What guide lines are there where you want to make restful calls to a given endpoint using different bearer tokens (or any authorization header) when calling the endpoint for multiple users?
private void CallEndpoint(string resourceId, string bearerToken) {
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("bearer", bearerToken);
var response = await httpClient.GetAsync($"resource/{resourceid}");
}
Given the above code could be called by any number of threads on a web application it is easily possible that the header set in the first line is not the same one that is used when calling the resource.
Without causing contention using locks and maintaining a stateless web application what is the recommended approach to creating and disposing HttpClients for a single endpoint (My current practice is to create a single client per endpoint)?
Lifecycle
Although HttpClient does indirectly implement the IDisposable
interface, the recommended usage of HttpClient is not to dispose of it
after every request. The HttpClient object is intended to live for as
long as your application needs to make HTTP requests. Having an object
exist across multiple requests enables a place for setting
DefaultRequestHeaders and prevents you from having to respecify things
like CredentialCache and CookieContainer on every request, as was
necessary with HttpWebRequest.
If your headers are usually going to be the same then you can set the DefaultRequestHeaders. But you don't need to use that property to specify headers. As you've determined, that just wouldn't work if you're going to have multiple threads using the same client. Changes to the default headers made on one thread would impact requests sent on other threads.
Although you can set default headers on the client and apply them to each request, the headers are really properties of the request. So when the headers are specific to a request, you would just add them to the request.
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);
That means you can't use the simplified methods that don't involve creating an HttpRequest. You'll need to use
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
documented here.
Some have found it helpful to use extension methods to isolate the code that updates the headers from the rest of a method.
Example of GET and POST methods done through an extension method that allow you to manipulate the request header and more of the HttpRequestMessage before it is sent:
public static Task<HttpResponseMessage> GetAsync
(this HttpClient httpClient, string uri, Action<HttpRequestMessage> preAction)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
preAction(httpRequestMessage);
return httpClient.SendAsync(httpRequestMessage);
}
public static Task<HttpResponseMessage> PostAsJsonAsync<T>
(this HttpClient httpClient, string uri, T value, Action<HttpRequestMessage> preAction)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = new ObjectContent<T>
(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null)
};
preAction(httpRequestMessage);
return httpClient.SendAsync(httpRequestMessage);
}
These could then be used like the following:
var response = await httpClient.GetAsync("token",
x => x.Headers.Authorization = new AuthenticationHeaderValue("basic", clientSecret));
Given that the .net HttpClient has been designed with reuse in mind and is intended to be long lived and memory leaks have been reported in short lived instances. What guide lines are there where you want to make restful calls to a given endpoint using different bearer tokens (or any authorization header) when calling the endpoint for multiple users?
private void CallEndpoint(string resourceId, string bearerToken) {
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("bearer", bearerToken);
var response = await httpClient.GetAsync($"resource/{resourceid}");
}
Given the above code could be called by any number of threads on a web application it is easily possible that the header set in the first line is not the same one that is used when calling the resource.
Without causing contention using locks and maintaining a stateless web application what is the recommended approach to creating and disposing HttpClients for a single endpoint (My current practice is to create a single client per endpoint)?
Lifecycle
Although HttpClient does indirectly implement the IDisposable
interface, the recommended usage of HttpClient is not to dispose of it
after every request. The HttpClient object is intended to live for as
long as your application needs to make HTTP requests. Having an object
exist across multiple requests enables a place for setting
DefaultRequestHeaders and prevents you from having to respecify things
like CredentialCache and CookieContainer on every request, as was
necessary with HttpWebRequest.
If your headers are usually going to be the same then you can set the DefaultRequestHeaders. But you don't need to use that property to specify headers. As you've determined, that just wouldn't work if you're going to have multiple threads using the same client. Changes to the default headers made on one thread would impact requests sent on other threads.
Although you can set default headers on the client and apply them to each request, the headers are really properties of the request. So when the headers are specific to a request, you would just add them to the request.
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);
That means you can't use the simplified methods that don't involve creating an HttpRequest. You'll need to use
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
documented here.
Some have found it helpful to use extension methods to isolate the code that updates the headers from the rest of a method.
Example of GET and POST methods done through an extension method that allow you to manipulate the request header and more of the HttpRequestMessage before it is sent:
public static Task<HttpResponseMessage> GetAsync
(this HttpClient httpClient, string uri, Action<HttpRequestMessage> preAction)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
preAction(httpRequestMessage);
return httpClient.SendAsync(httpRequestMessage);
}
public static Task<HttpResponseMessage> PostAsJsonAsync<T>
(this HttpClient httpClient, string uri, T value, Action<HttpRequestMessage> preAction)
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = new ObjectContent<T>
(value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null)
};
preAction(httpRequestMessage);
return httpClient.SendAsync(httpRequestMessage);
}
These could then be used like the following:
var response = await httpClient.GetAsync("token",
x => x.Headers.Authorization = new AuthenticationHeaderValue("basic", clientSecret));
I have the following set up:
JS client -> Web Api -> Web Api
I need to send the auth cookie all the way down. My problem is sending it from one web api to another. Because of integration with an older system, that uses FormsAuthentication, I have to pass on the auth cookie.
For performance reasons I share a list of HttpClients (one for each web api) in the following dictionary:
private static ConcurrentDictionary<ApiIdentifier, HttpClient> _clients = new ConcurrentDictionary<ApiIdentifier, HttpClient>();
So given an identifier I can grab the corresponding HttpClient.
The following works, but I'm pretty sure this is bad code:
HttpClient client = _clients[identifier];
var callerRequest = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
string authCookieValue = GetAuthCookieValue(callerRequest);
if (authCookieValue != null)
{
client.DefaultRequestHeaders.Remove("Cookie");
client.DefaultRequestHeaders.Add("Cookie", ".ASPXAUTH=" + authCookieValue);
}
HttpResponseMessage response = await client.PutAsJsonAsync(methodName, dataToSend);
// Handle response...
Whats wrong about this is that 1) it seems wrong to manipulate DefaultRequestHeaders in a request and 2) potentially two simultanious requests may mess up the cookies, as the HttpClient is shared.
I've been searching for a while without finding a solution, as most having a matching problem instantiates the HttpClient for every request, hence being able to set the required headers, which I'm trying to avoid.
At one point I had get requests working using a HttpResponseMessage. Perhaps that can be of inspiration to a solution.
So my question is: is there a way to set cookies for a single request using a HttpClient, that will be safe from other clients using the same instance?
Instead of calling PutAsJsonAsync() you can use HttpRequestMessage and SendAsync():
Uri requestUri = ...;
HttpMethod method = HttpMethod.Get /*Put, Post, Delete, etc.*/;
var request = new HttpRequestMessage(method, requestUri);
request.Headers.TryAddWithoutValidation("Cookie", ".ASPXAUTH=" + authCookieValue);
request.Content = new StringContent(jsonDataToSend, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
UPDATE:
To make sure that your HTTP client does not store any cookies from a response you need to do this:
var httpClient = new HttpClient(new HttpClientHandler() { UseCookies = false; });
Otherwise you might get unexpected behavior by using one client and sharing other cookies.
In Asp.Net Web Api 2 what is the difference between setting an HttpClient Accept Header using the following traditional method :
HttpClient client = HttpClientFactory.Create(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
and the following method :
var headers = new Dictionary<string, string>
{
{"Accept", "application/json"}};
headers.ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));
Update 1:
Based on the answer by #DarrenMiller in the following post What is the overhead of creating a new HttpClient per call in a WebAPI client? it appears that the preferred method is using DefaultRequestHeaders property because it contains properties which are intended for multiple calls. Does this mean if I set a default header using a simple dictionary my HttpClient client will not be as efficient as the one which uses DefaultRequestHeaders? In addition I really cant understand how the values inside DefaultRequestHeaders will be reused? Lets say I create 20 HttpClient client using HttpClientFactory.Create and inside every single one of them I set DefaultRequestHeaders property [Do I really need to do it because DefaultRequestHeaders was meant to be reused?!]. Where does this reuse kick-in and does setting the DefaultRequestHeaders every time I create a HttpClient client result in some kind of performance hit?
Part one of your question: Is there any difference for adding headers?
HttpClient client = HttpClientFactory.Create(handler);
Method 1:
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Method 2:
var headers = new Dictionary<string, string>{{"Accept", "application/json"}};
headers.ForEach(h => client.DefaultRequestHeaders.Add(h.Key, h.Value));
Method 1 gives you nice strongly typed values with the ability to add multiple accept types. Method 2 has one more "magic string" that could be a place for typos and there is no way to add multiple accept types.
Part 2 of your question: Where is the performance and reuse value?
The performance hit of using a new HttpClient for every request depends on your use case. Get a bench mark and measure to see if it matters. The performance on the developer is most likely where the gains will be. Consider that every HttpClient you use you have to remember a bunch of headers to add. If you forget to add a proper header, errors happen. So, you can use DefaultRequestHeaders to set these up in a factory.
public class ApiService
{
public static HttpClient GetClient()
{
var client = new HttpClient(new Uri("https://someservice/"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//add any other setup items here.
return client;
}
}
Now use it:
public async Task DoStuff()
{
using(var client = ApiService.GetClient())
{
//client will have the proper base uri and all the headers set.
var data = await client.GetAsync<dynamic>("Sales");
//client will still have the proper base uri and all the headers set.
var data2 = await client.GetAsync<dynamic>("Products");
}
}
HttpClients should be short lived and always wrapped in a using statement. The reuse occurs when multiple requests are made using the same client.
UPDATE:
As others have mentioned, Microsoft recommends keeping one HttpClient for the life of the application to avoid excess overhead. The preferred way to get an HttpClient would be having a static one declared and then reference that as needed. Be aware that anything set in the default headers will go out with every request so make sure you don't put things there such as Authorization unless you're sure of the final destination of the request.
I'm currently using HttpWebRequest to get a website. I'd like to use the await pattern, which is not given for HttpWebRequests. I found the class HttpClient, which seems to be the new Http worker class. I'm using HttpClient.GetAsync(...) to query my webpage. But I'm missing the option to add ClientCredentials like HttpWebRequest.Credentials. Is there any way to give the HttpClient authentication information?
You can pass an instance of the HttpClientHandler Class with the credentials to the HttpClient Constructor:
using (var handler = new HttpClientHandler { Credentials = ... })
using (var client = new HttpClient(handler))
{
var result = await client.GetAsync(...);
}
You shouldn't dispose of the HttpClient every time, but use it (or a small pool of clients) for a longer period (lifetime of application. You also don't need the handler for it, but instead you can change the default headers.
After creating the client, you can set its Default Request Headers for Authentication. Here is an example for Basic authentication:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "username:password".ToBase64());
ToBase64() represents a helper function that transforms the string to a base64 encoding.