Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 566
Nagoh Avatar asked Sep 29 '14 22:09

Nagoh


People also ask

DOES GET request have Content-Type header?

In the GET response below, the Content-Type header field specifies the type of data contained in the response body. By convention, a POST request typically asks to create something. The data required to fulfill the request is carried in the request body.

Is Content-Type required for GET request?

Since (by validity of the input spec) there are no content types for GET requests, you will always default to application/json .

What is Content-Type in request header?

The Content-Type representation header is used to indicate the original media type of the resource (prior to any content encoding applied for sending). In responses, a Content-Type header provides the client with the actual content type of the returned content.


2 Answers

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.

like image 99
Faris Zacina Avatar answered Oct 13 '22 17:10

Faris Zacina


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;
    }
}
like image 22
Joannes Vermorel Avatar answered Oct 13 '22 17:10

Joannes Vermorel