Why I cannot set 'Allow' in HTTP response header? - c#

I've written a RESTful API using ASP.NET Web Api. Now I'm trying to make it returns the allowed verbs for a controller. I'm trying to do it with the following code:
[AcceptVerbs("OPTIONS")]
public HttpResponseMessage Options()
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "POST");
response.Headers.Add("Allow", "POST");
return response;
}
But instead of getting a Allow Header on my response, I'm getting a 500 Internal Server Error. While debugging I receive the following error:
{"Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."}
Is that possible to set that header?

As the error message says, you must use content headers with HttpContent objects.
response.Content.Headers.Add("Allow", "POST");
Must admit this is kinda weird API...

Allow is a content header.
response.Content.Headers.Allow.Add("POST");

Related

Bitkub Custom Headers in PostRequest C#

I am making a Post HttpRequest, but although I added Headers to the request message the response I am getting has a field RequestMessage which doesn't contain the Headers I added.
request.Headers.Add("Accept", "application/json");
request.Headers.Add("X-BTK-APIKEY", _apiKey._public_key);
I am using PostAsync and HttpRequestMessage.Headers.Add to add custom headers to the secure endpoints of Bitkub, but I am getting {"error": 2} (Missing X-BTK-APIKEY) as return. I've tried:
Changing PostAsync to SendAsync
Changing request.Headers.Add("X-BTK-APIKEY", _apiKey._public_key) to request.Headers.Authorization = AuthenticationHeaderValue("X-BTK-APIKEY", _apiKey._public_key).
But the results are similar.

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);
}

Get response in JSON from API

I am using HttpClient to consume an external API from an ASP.NET Web API controller. I am not using authentication, just a token, so I have:
using (var httpClient = new HttpClient()) {
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync(endpoint);
}
I am getting the response always in XML format but I am sending header with "application/json".
Am I missing something it this is a problem with the external API?
What else can I try to get the response in JSON?
It's up to the API developer(s) to respect the media type (application/json). It is possible for a developer to explicitly return XML when a client requests JSON (if they feel like trolling), though in this case it is probably just giving you the default format because they don't check the header value.
Check the docs or contact them directly to confirm they return data in JSON format and how to format the request to get JSON.
You should set Accept: application/json as well as Content-Type: application/json.

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.

ASP.NET 415 Unsupported Media Type error when HttpRequestMessage Content is touched

I created a message handler for my ASP.NET Web API which I would like to read the HttpRequestMessage content, make some modifications, then set it to the modified content before passing it on. However, this results in a 415 Unsupported Media Type error even if no modifications are made. This code reproduces the problem:
HttpRequestMessage request = (input to handler)
String body = request.Content.ReadAsStringAsync().Result;
request.Content = new StringContent(body);
How can I touch the HttpRequestMessage content without getting a 415 error? I know content is supposed be read-once, but I am replacing it with new content. Is there a read flag which needs to be reset somewhere?
I found this neglected answer on a different question: https://stackoverflow.com/a/20262394/2852699
Here is the modified code for my example.
HttpRequestMessage request = (input to handler)
MediaTypeHeaderValue contentType = request.Content.Headers.ContentType;
String body = request.Content.ReadAsStringAsync().Result;
(modify body here)
request.Content = new StringContent(body);
request.Content.Headers.ContentType = contentType;
This lets me read, modify, then write the content without causing any errors. The default ContentType for StringContent is "text/plain; charset=utf-8" while the ContentType I needed was "application/json; charset=UTF-8".
while posting any data to the api please make sure to send the content-type also with the header as below:
Content-Type: application/json; charset=UTF-8;
here, data being sent is in json format and in UTF-8 format.
API needs to understand the data type and data format both in order to convert it to an appropriate data type.

Categories