Content Headers Remove fails for string Authorization - c#

The following test fails inexplicably:
[Test]
public void CrazyAssHttpRequestMessageTest()
{
var subject = new HttpRequestMessage()
{
Method = HttpMethod.Get,
Content = new StringContent("some content")
};
subject.Content.Headers.Remove("Authorization");
}
The exceptions is:
System.InvalidOperationException : Misused header name. Make sure
request headers are used with HttpRequestMessage, response headers
with HttpResponseMessage, and content headers with HttpContent
objects.
Why? Any other header seems to work fine, replace Authorization with something else and all is ok.

The HttpContentHeaders class only supports a subset of HTTP headers -- the headers relating to content. It seems a bit of an odd decision to split them up that way, but that's the way the framework works.
The upshot is that there will never be an Authorization header in request.Content.Headers.
You get exactly the same error if you try to remove "Content-Type" from HttpRequestHeaders or HttpResponseHeaders, or if you try to add an unexpected header to these collections without calling TryAddWithoutValidation. Even more frustrating is the fact that Contains() will throw if you try to check for an invalid header. You can check for existence without throwing without worrying about the exact type of header collection using HttpHeaders.TryGetValues, or just use request.Content.Headers.Any(x => x.Key == "Authorization").
The classes linked above have a list of the headers they explicitly support (as strongly typed properties) e.g. HttpContentHeaders.ContentType.

Related

Add content-type header to httpclient GET request

I am trying to add Content-Type header to HttpClient GET request, here my code:
HttpClient client=new ....
bool added = client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded");
var response = await client.GetAsync(...
but the added variable is false, i.e it failed to add the header.
How can I add this header?
NOTE:
This post deals with POST request, I asked about GET
If you look at the Http/1.1 specification:
A sender that generates a message containing a payload body SHOULD
generate a Content-Type header field in that message unless the
intended media type of the enclosed representation is unknown to the
sender. If a Content-Type header field is not present, the recipient
MAY either assume a media type of "application/octet-stream"
([RFC2046], Section 4.5.1) or examine the data to determine its type.
Check also the MDN on get requests
The HTTP GET method requests a representation of the specified resource. Requests using GET should only retrieve data.
Sending body/payload in a GET request may cause some existing implementations to reject the request — while not prohibited by the specification, the semantics are undefined. It is better to just avoid sending payloads in GET requests.
Effectively, that means that wether you send or not the header, it's going to be ignored and/or rejected.
When setting the content type, it's better to set it from the content itself: How do you set the Content-Type header for an HttpClient request?
Im currently working on a project, where I call an api using a POST request.
This might help in your case. Its how its done in an official Microsoft Documentation.
using (var content = new ByteArrayContent(byteData))
{
// This example uses the "application/octet-stream" content type.
// The other content types you can use are "application/json"
// and "multipart/form-data".
content.Headers.ContentType = new mediaTypeHeaderValue("application/octet-stream");
response = await client.PostAsync(uriBase, content);
}

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.

HTTP request whose headers can be controlled and is automatically decompressed

I'm trying to send HTTP requests in C# that look like HTTP requests from a certain software. I wanted to use System.Net.HttpWebRequest but it doesn't give me the control I need over its headers: their letter-casing can't be changed (e.g. I want the Connection header to be keep-alive and not Keep-Alive), I don't have full control over the headers ordering, etc.
I tried using HttpClient from CodeScales library. Unfortunately, it doesn't decompress responses automatically (see HttpWebRequest.AutomaticDecompression). I decompressed it myself with System.IO.Compression.GZipStream and DeflateStream, but it didn't work when the response had the header Transfer-Encoding: chunked.
System.Net.Http.HttpRequestHeaders seems to give more control over headers than HttpWebRequest, but still not enough.
How can it be done?
Edit: I know that HTTP accepts those headers as valid anyway, but I'm working with a server that validates the headers and refuses to respond if they're not exactly what it expects.
To set some headers in the HTTPWebRequest class, you have to either use an attribute from the class (for example HttpWebRequest.KeepAlive = true), or you have to add the custom header to the request by calling the add method to the request headers.
Something important is that is you try to add the header (in a custom way) while it's already an attribute of the request, it'll send you an error.
objRequest.Headers.Add("Accept", "some data");
is incorrect. You'd rather say.
objRequest.Accept = "some data";
In your case you can :
objRequest.KeepAlive = true;
Don't worry to much for the letter-casing, it doesn't matter as far as you're sending the appropriate headers to the server.

Add request headers with WebClient C#

I have the following code with which I download a web-page into a byte array and then print it with Response.Write:
WebClient client = new WebClient();
byte[] data = client.DownloadData(requestUri);
/*********** Init response headers ********/
WebHeaderCollection responseHeaders = client.ResponseHeaders;
for (int i = 0; i < responseHeaders.Count; i++)
{
Response.Headers.Add(responseHeaders.GetKey(i), responseHeaders[i]);
}
/***************************************************/
Besides of the response headers, I need to add request headers as well. I try to do it with the following code:
/*********** Init request headers ********/
NameValueCollection requestHeaders = Request.Headers;
foreach (string key in requestHeaders)
{
client.Headers.Add(key, requestHeaders[key]);
}
/***************************************************/
However it does not work and I get the following exception:
This header must be modified using the appropriate property.Parameter name: name
Could anybody help me with this? What's the correct way of adding request headers with WebClient?
Thank you.
The headers collection "protects" some of the possible headers as described on the msdn page here: http://msdn.microsoft.com/en-us/library/system.net.webclient.headers.aspx
That page seems to give all the answer you need but to quote the important part:
Some common headers are considered restricted and are protected by the
system and cannot be set or changed in a WebHeaderCollection object.
Any attempt to set one of these restricted headers in the
WebHeaderCollection object associated with a WebClient object will
throw an exception later when attempting to send the WebClient
request.
Restricted headers protected by the system include, but are not
limited to the following:
Date
Host
In addition, some other headers are also restricted when using a
WebClient object. These restricted headers include, but are not
limited to the following:
Accept
Connection
Content-Length
Expect (when the value is set to "100-continue"
If-Modified-Since
Range
Transfer-Encoding
The HttpWebRequest class has properties for setting some of the above
headers. If it is important for an application to set these headers,
then the HttpWebRequest class should be used instead of the WebRequest
class.
I suspect the reason for this is that many of the headers such as Date and host must be set differently on a different request. You should not be copying them. Indeed I would personally probably suggest that you should not be copying any of them. Put in your own user agent - If the page you are getting relies on a certain value then I'd think you want to make sure you always send a valid value rather than relying on the original user to give you that information.
Essentially work out what you need to do rather than finding something that works and doing that without fully understanding what you are doing.
Looks like you're trying to set some header which is must be set using one of the WebClient properties (CachePolicy, ContentLength or ContentType)
Moreover, it's not very good to blindly copy all the headers, you need to get just those you really need.

Categories