HttpClient single instance with user specific request headers. (Concurrently) - c#

I am reusing same HttpClient throughout the application. But i have to set different headers for different users.
I have referred this post : HttpClient single instance with different authentication headers
When I implemented this approach and runs code concurrently, like this :
Parallel.For(0, 3, i =>
{
// HttpClient Call
}
Thread overrides each other data. Could you please let me know how to fix it ?
Thanks in Advance.

Related

Can't use GetStringAsync() if I want to change the Referer header each time?

It seems that HttpClient is the recommended way to do HTTP communication. Downloading HTML of a URL seemed easy like
var html = httpClient.GetStringAsync(url);
But I need to change some header values like Referer each time. At first, I tried
httpClient.DefaultRequestHeaders.Add("Referer", referrer);
, but this caused an error at the second time. It seems that it is once set, cannot be changed.
I searched for a solution and found one ( https://stackoverflow.com/a/12023307/455796 ), but this seems very complicated than GetStringAsync. I need to create a HttpRequestMessage, call SendAsync, continue to call response.Content.ReadAsAsync, call Wait(), and then read the result. Also, the comment says I need to dispose the HttpRequestMessage. If this is the only way to change headers, I will do so, but is this the best way? Can't I use GetStringAsync and change a header value?
You could do it this way:
using(var msg = new HttpRequestMessage(HttpMethod.Get, new Uri("https://www.test.com")))
{
msg.Headers.Referrer = new Uri("https://www.example.com");
using (var req = await _httpClient.SendAsync(msg))
{
var str1 = await req.Content.ReadAsStringAsync();
}
}
or, if you want to override the default request headers, do it this way:
_httpClient.DefaultRequestHeaders.Referrer = new Uri("https://www.example.com");
var str2 = await _httpClient.GetStringAsync("https://www.test.com/");
The first way is if you want to reuse your HttpClient throughout the lifetime of your application. Meaning, if you share it with every object or method that needs to make an HTTP request. Doing it this will not cause conflicts if multiple threads are trying to use it at the same time when each thread is modifying headers. DeafaultRequestHeaders is not thread-safe... so you could get exceptions thrown if more than one thread is modifying the DeafaultRequestHeaders property at the same time.

Single HttpClient for application life cycle - How does this single instance of HttpClient ensures that it has responded to correct request?

I have created a single instance of HttpClient in Application_Start event to be reused accross the application in Global.asax.cs
Code in App start:
protected new void Application_Start()
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
string _accessTokenUrl = ConfigurationManager.AppSettings["KongAccessTokenURl"];
string _adminUrl = ConfigurationManager.AppSettings["KongAdminUrl"];
base.Application_Start();
ApplicationWrapper.KongAdminClient = new HttpClient(httpClientHandler)
{
BaseAddress = new Uri(_adminUrl)
};
}
Here ApplicationWrapper.KongAdminClient is a static property.
I have developed a login API and within that api i Invoke Kong gateway api to generate token so that i can create a response with that token for that particular user.
For above purpose i create a new HttpRequestMessage for each request but HttpClient remains same as Microsoft says ..
HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads
https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.8#remarks
My question is that with this same intance how will HttpClient know which thread to respond to ?
will this same instance respond appropriately to correct requesting thread under load conditions?
Think about it this way. When you are using the Math.Round function, you are effectively just calling a function that does something - in this case rounding - based on a specific input.
It might have some constants and other values reused, but they don't change in a way that affects other calls.
So when you use code like GetAsync you are just calling a method that gets some input and returns a value.

Handling HTTPClient performing multiple requests in fire and forget

I have to make multiple Http post requests (few hundreds or can be more) and not wait for any of the responses as have an SLA. Without waiting for those response I need to send back response from my Web API (before performing mentioned multiple HTTP requests, I fetch data from another API and need to return back).
I have looked around and found "fire and forget" implementation which does not wait for response. I am not sure if this right way to do and since im returning without waiting for parallel fire and forget requests, how will HttpClient get disposed?
HttpClient client = new HttpClient();
var CompositeResponse = client.GetAsync(_SOMEURL);
List<MEDLogresp> sortedmeds = MEDLogresps.OrderBy(x => x.rxId).ThenBy(y => y.recordActionType);
Task.Run(() => Parallel.ForEach(sortedmeds, ele => clientMED.PostAsync(URL , new StringContent(JsonConvert.SerializeObject(ele), Encoding.UTF8, "application/json"))));
return ResponseMessage(Request.CreateResponse<CompositeResponse>(HttpStatusCode.OK, compositeResponse));
since im returning without waiting for parallel fire and forget
requests, how will httpclient get disposed?
You can use a single, shared static instance of HttpClient for the lifetime of your application and never Dispose() it. It is safe to use the same HttpClient from multiple threads concurrently.

Is setting the Authorization header in HttpClient safe?

I'm working in a MVC5 ASP.NET project, and learned that to send authenticated requests to a WEB API from the controller I could do the following to add a token to the header(using an example code):
public static class APICaller
{
// Use a single instance for HttpClient to reduce overhead
private static readonly HttpClient client = new HttpClient();
//Set the Authorization Header
public static string SetHeader( string token )
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return("Success");
}
}
Is setting the header this way on the HttpClient thread-safe? Will other users have a way to access this same token, given that there is only one instance of this HttpClient?
EDIT:
I'd like to ask one more question to get a better understanding of how it works. Would I need to add the header each time I'm making a request with the same HttpClient object?
With the approach you have, once you've set the default request header on your static instance, it will remain set without you having to keep setting it. This means that if you have multiple requests coming into your server, you could end up in a situation where the header is set for one user and then changed by another request before that first request makes it out the door.
One option to avoid this would be to use SendAsync when using user-specific authorisation headers. This allows you to tie the header to a specific message, rather than setting it as a default for the HttpClient itself.
The code is a bit more verbose, but would look something like this:
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://path/to/wherever"))
{
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "TheToken");
using (var httpResponseMessage = httpClient.SendAsync(httpRequestMessage))
{
// ...
}
}
As you can see, the header is set specially on each request and therefore the issue of mixing up the headers goes away. The obvious downside is that this syntax is more verbose.
Will other users have a way to access this same token, given that there is only one instance of this HttpClient?
Yes, that is why you need to be careful when setting the default headers.
Would I need to add the header each time I'm making a request with the same HttpClient object?
No, because you set the default header all requests created with that object will have the header.
For things like a Bearer token it is better to not put in the default headers and instead put it in the request header by creating a new HttpRequestMessage object, setting the headers you need there, then using HttpClient.SendAsync( passing in the request message to send the headers along with your request.

HttpClient DeleteAsync with Multiple records

I'm using the sendgrid api here:
https://sendgrid.com/docs/API_Reference/Web_API_v3/Marketing_Campaigns/contactdb.html#Delete-a-Recipient-DELETE
and it shows passing an array of strings to the DELETE call. When I look at the signature of System.Net.Http.HttpClient, DELETE does not allow for content to be passed in.
Is there a standard around DELETE that does not allow for multiple content passed at the same time?
API definition:
The HTTP/1.1 RFC states that a DELETE request's payload has no defined semantics.
It's not illegal to include a payload, but this means that if a payload is included, it should be ignored.
Many HTTP clients, such as the one provided by the .NET framework, don't provide an interface to include a payload when it has no defined semantics for the method.
Unfortunately, many REST APIs do require a payload with these methods. You can accomplish this by manually creating a HttpRequestMessage object, setting the Method and Content properties, and passing it to the HTTP client's SendAsync method.
Create an extension method
public static class HttpClientExtensions
{
public static Task<HttpResponseMessage> Delete(this HttpClient client, HttpContent content)
{
var request = new HttpRequestMessage { Method = "DELETE", Content = content);
return client.SendAsync(request);
}
}
However I cannot recommend it, as it breaks basic assumptions of HTTP, which allows efficient HTTP Proxies to work.
The "correct method" around this problem is to use HTTP 2.0 (or HTTP 1.1 Pipelining, which is deprecated due to it being mostly broken, but you could try it out) to create multiple DELETE requests. In theory that solution does not require any code change.

Categories