Any hooks for HttpClient? - c#

When making a REST call, the usual approach with C# is:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.SendAsync(....);
}
But what if I want every single outbound request to have a custom header, without manual coding? With a WCF proxy I can achieve that result by adding a global endpoint behavior (e.g. IMessageInspector) which will hook every outbound call.
With HttpClient, I don't think such a hook point exists. Perhaps the best I can do is to create an extension method for HttpClient which automatically adds the header. Unfortunately, that means that every developer must still voluntarily comply and remember to manually invoke the extension method.
Any solution I'm overlooking?
p.s. I understand that REST is supposed to be light-weight, and so much of the abstraction of WCF is jettisoned. Still, no harm in asking...

You could use a custom HttpMessageHandler:
public class YourServiceMessageHandler : HttpClientHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Add header to request here
return base.SendAsync(request, cancellationToken);
}
}
And pass it to the HttpClient constructor:
HttpClient client = new HttpClient(new YourServiceMessageHandler());

No hooks like that, but if you are writing a HTTP API, you can just hide the entire uploading process in your class so the users of your API only call something like SendData(dataPackage);, and instead of using HttpRequest in your extension as a parent, you just create an instance of it so others don't see what you are doing.

Related

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.

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.

Explicitly Set Content-Type Headers For Get Operation in HttpClient

Is there a way in which I can explicitly set the Content-Type header values when performing a GET with HttpClient ?
I realise this breaks 1.1 protocol, but I am working with a API that does not conform to it, and REQUIRES I set a Content-Type Header.
I have tried this with to no avail...
using (var httpClient = new HttpClient())
{
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.com");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded+v1.3");
await httpClient.SendAsync(httpRequestMessage)
}
I've inspected the DefaultRequestHeaders after the TryAddWithoutValidation is added, and it does not seem to be setting the Content-Type value.
If I try to set the Content-Type of the httpRequestMessage (by setting httpRequestMessage.Content = ..., I get the following error:
Cannot send a content-body with this verb-type.
Is there a way that I can explicitly set the Content-Type for a GET operation using the HttpClient?
Based on my findings i concluded the HttpClient is very restrictive in terms of the protocol rules. I also reflected through the implementation DLL and i couldn't find anything that it would indicate that it allows protocol violations.
GET requests shouldn't have content-type headers, and the HttpClient is enforcing that rule.
I think the exception message when you try to set the content-type header is self-descriptive:
System.InvalidOperationException: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.
Also if you use set the content body you get one more self-descriptive message:
System.Net.ProtocolViolationException: Cannot send a content-body with this verb-type.
Since you are willing to violate HTTP rules for GET requests i am pretty sure your only option is to stick with the less restrictive WebClient, which works in that scenario.
It's possible - and very dirty - to override the library behavior with a bit of reflection and by introducing a DelegatingHandler that you give as argument to the HttpClient constructor. See the code below.
public class HmacAuthenticatingHandler : DelegatingHandler
{
public HmacAuthenticatingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// HACK: Set 'Content-Type' even for GET requests
var invalidHeaders = (HashSet<string>)typeof(HttpHeaders)
// use "_invalidHeaders" for System.Net.Http v2.2+
.GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(request.Headers);
invalidHeaders.Remove("Content-Type");
request.Headers.Remove("Content-Type");
request.Headers.Add("Content-Type", "application/json");
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Although of no help to you right now, it does look like a future release of the .NET framework may support this type of protocol violation with the addition of an AddWithoutValidation method:
https://msdn.microsoft.com/en-us/library/hh204926
Have you tried adding headers to content header (as apposed as a request header)
see here
I came across the same situation with an API I need to call, and I was able to work around it by setting the content to an empty StringContent:
httpRequestMessage.Content = new StringContent("", Encoding.ASCII, "application/json");
This sends both a Content-Type and a Content-Length header (with value of 0), which the API I'm calling is ok with. This wouldn't work if the API rejects requests with a Content-Length header.
I'm using .NET Core 3.1. It looks like the version used by OP did not support setting the Content property on a GET request.

Modify request headers per request C# HttpClient PCL

I'm currently using the System.Net.Http.HttpClient for cross platform support.
I read that it is not a good practice to instantiate a HttpClient object for each request and that you should reuse it whenever possible.
Now I have a problem while writing a client library for a service. Some API calls need to have a specific header, some MUST not include this specific header.
It seems that I can only manipulate the "DefaultRequestHeaders" which will be send with each request.
Is there an option when actually making the request with e.g. "client.PostAsync()" to modify the headers only for the specific request?
(Info: Requests can be multi threaded).
Thanks in advance!
Yes, you can create a new HttpRequestMessage, set all the properties you need to, and then pass it to SendAsync.
var request = new HttpRequestMessage() {
RequestUri = new Uri("http://example.org"),
Method = HttpMethod.Post,
Content = new StringContent("Here is my content")
}
request.Headers.Accept.Add(...); // Set whatever headers you need to
var response = await client.SendAsync(request);
Use HttpContent.Headers. Simply create HttpContent instance with required headers and pass it to PostAsync method.

Categories