HttpClient.SendAsync sending wrong Content-Type - c#

HttpClient httpClient = new HttpClient();
MyRequest request = new MyRequest (data);
var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(request), System.Text.Encoding.UTF8, "application/json");
HttpRequestMessage httpRequestMessage = new HttpRequestMessage
{
RequestUri = new Uri("http://localhost:8000/api/action"),
Content = content,
Method = HttpMethod.Post
};
httpRequestMessage.SetBrowserRequestMode(BrowserRequestMode.NoCors);
await httpClient.SendAsync(httpRequestMessage);
Using HttpClient in Blazor WebAssembly I am trying to send a request to an API.
However, despite specifying application/json as the content type it sends text/plain;charset=UTF-8 (as viewed in the Chrome Network tab). This results in the API throwing an error.

I think you could check these caseļ¼š
case1,case2
read this document,and try with PostAsJsonAsync method
I tested as below and worked well:
var weatherforecast = new WeatherForecast() { Date = DateTime.Now, Summary = "testsummary", TemperatureC = 44 };
var response = await Http.PostAsJsonAsync("https://localhost:44385/WeatherForecast", weatherforecast);
Result:

Related post:
Wrong Content-Type being substituted for fetch http request
HttpClient in WebAssembly calls the standard fetch method.
As per the fetch specification when using no-cors only a limited number of content-types can be used:
https://fetch.spec.whatwg.org/#simple-header
"application/x-www-form-urlencoded"
"multipart/form-data"
"text/plain"
The preferred solution would be to correctly configure the end point you are calling to allow cross origin requests and not to use no-cors e.g:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
Access-Control-Allow-Origin: *
If this is not possible (as in my case) and you must use no-cors the only other option would be to change your end point to map "text/plain" to "application/json"
Whilst many may not consider this a bug it is an inconsistency in how HttpClient behaves and is not obvious (though the NoCors option is only available in WebAssembly)

Related

C# HttpClient cannot send authorization header to ASP.NET Core Web API

I am trying to use C# HttpClient from ASP.NET MVC to make a request to an API. My API is running on .NET 6.0.
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"{token}");
var serialized = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync(urlToSend, serialized);
Here is my code. I tried all the possibilities I saw on google. But when sending request, I can't send Authorization header.
I can send it with Postman.
Here is my API code:
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IResult))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(IResult))]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword([FromBody] ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return GetResponseOnlyResult(await Mediator.Send(changePasswordCommand));
}
Note: In my _url, I use http, not https.
I'm not sure but maybe the [AllowAnonymous]attribute remove the Authorization header from request just because it does not make sense if no authorization is needed.
Have you checked if the sent request contains the header using a tool like fiddler ?
I solved the problem by changing my base url from HTTP to HTTPS.
I tried with Fiddler and I got the same problem when I request to HTTP.
So thanks to #olivier-duhart .
To add to the accepted answer, the problem gets solved by changing from HTTP to HTTPS is due to the fact that, the Authorization header gets stripped during redirects.
This behavior is for security concerns and is by design, as mentioned in the github discussion here.
The same behavior may not be seen when using Postman vs HttpClient for example, is due to the way that different clients, have differing mechanisms, by which the subsequent requests (following a response status 30X) to the redirect location are handled.
Also a great answer elsewhere on stackoverflow : Authorization header is lost on redirect
Please review this link. Allow Anonymous will ignore the authentication header
https://github.com/dotnet/aspnetcore/issues/30546
I tried with the code. It seems working fine for me. Here is my code of console app
try
{
ChangePasswordCommand passobj = new ChangePasswordCommand() { password = "new password"};
string _url = "https://localhost:44395/api/Values/";
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(_url);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue($"Bearer", $"MYTOKEN");
var serialized = new StringContent(JsonConvert.SerializeObject(passobj), Encoding.UTF8, "application/json");
var httpResponseMessage = await httpClient.PutAsync("changeuserpassword", serialized);
}
catch (Exception ex) {
}
And here is controler Api
[AllowAnonymous]
[Consumes("application/json")]
[Produces("application/json", "text/plain")]
[HttpPut("changeuserpassword")]
public async Task<IActionResult> ChangeUserPassword(ChangePasswordCommand changePasswordCommand)
{
var accessToken = Request.Headers[HeaderNames.Authorization];
return Ok();
}

Sending custom Content-Type using HttpClient C# .Net6

Hello Stackoverflow community. I hope someone here can help me!!
I'm trying to integrate with the Zoopla API that requires the post request to send the following customized content type. (I've got the certificate side of things working fine).
application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json
I've tried the following approaches without any success (they all result in the following error)
System.FormatException: 'The format of value 'application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json' is invalid.'
Initial approach was to set it within the content of the RequestMessage
var request = new HttpRequestMessage()
{
RequestUri = new Uri("https://realtime-listings-api.webservices.zpg.co.uk/sandbox/v1/listing/list"),
Method = HttpMethod.Post,
Content = new StringContent(jsonBody, Encoding.UTF8, "application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json")
};
When that didn't work I tried to set it via the default headers (the client below is from the ClientFactory)
client.DefaultRequestHeaders.Add("Content-Type", "application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json");
My final attempt was to set it without validation
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json");
I've just tried something else which unfortunately didn't work
string header = "application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json";
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(header));
I am well and truly stumped!! HELP!! :-)
Content-Type is set on the content, not in DefaultRequestHeaders. You may try using TryAddWithoutValidation on the request content:
var content = new StringContent("hello");
content.Headers.ContentType = null; // zero out default content type
content.Headers.TryAddWithoutValidation("Content-Type", "application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json");
var client = new HttpClient(); // note: use IHttpClientFactory in non-example code
var response = await client.PostAsync("https://postman-echo.com/post", content);
Console.WriteLine(response.StatusCode); // OK
Console.WriteLine(await response.Content.ReadAsStringAsync());
// {"args":{},"data":{},"files":{},"form":{},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-6345b568-22cc353761f361483f2c3157","content-length":"5","content-type":"application/json;profile=http://realtime-listings.webservices.zpg.co.uk/docs/v1.2/schemas/listing/list.json"},"json":null,"url":"https://postman-echo.com/post"}

ASP.net MVC clients consume patch API get Bad request 400 returns

I'm using asp.net MVC 5 to consume API that also developed in asp.net MVC.
For POST and GET requests, I managed to make it work, except for PATCH that always get 400 bad request from web service.
This is what I do in my client controller:
using (HttpClient httpClient1 = new HttpClient())
{
string apiURLGetClientApproval = "/clients/approvals?action=" + actionType;
HttpMethod method = new HttpMethod("PATCH");
HttpRequestMessage message = new HttpRequestMessage(method, new Uri(baseAddress + apiURLGetClientApproval));
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient1.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token.AccessToken);
message.Content = content;
var result = httpClient1.SendAsync(message).Result;
}
This is the content that I pass from my client to API:
{{"clients": [
{
"cn": "1132196",
"hitdate": "04/05/2021"
}]}}
PS :
I access API by postman and ajax from client side with this content, got success reponse.
I have tried with these solution, but same 400 error bad request responsed : PATCH Async requests with Windows.Web.Http.HttpClient class
This is how the parameter of API look like:
[CustomAuth(Roles = "Super Admin, Admin, User")]
[HttpPatch]
[Route("clients/approvals")]
public HttpResponseMessage UpdateClientApproval(HttpRequestMessage request, string action, [FromBody]JObject data)
{..... }
I have been dealing with the same exact problem for 2 days now. I just fixed it. I realised that sending a PATCH request probably required some specific payload [{"op":"replace"....}] as we can tell from using PostMan. However the PUT request doesn't, in fact most of the data on the business object would already be populated, so you modify what you want to change and send a PUT request instead. I just did that. I had to add the PUT action method in my controller and change the HttpClient to send a PUT request and it worked less than 5mins ago.

404 when accessing Design Automation API v3 through HttpClient

Running calls to the Design Automation API in Postman works just fine but when I try to make the same calls in C# using HttpClient they fail with a 404 that seems to actually hide an authentication error:
{
"developerMessage":"The requested resource does not exist.",
"userMessage":"",
"errorCode":"ERR-002",
"more info":"http://developer.api.autodesk.com/documentation/v1/errors/err-002"
}
That link leads to an authentication error:
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>1F52E60A45AEF429</RequestId>
<HostId>
[ Some base64 ]
</HostId>
</Error>
I'm following examples for how to use HttpClient, but I may be missing something. I successfully get the access token, run
var client = new HttpClient
{
BaseAddress = new Uri("https://developer.api.autodesk.com/da/us-east")
};
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue(TokenType, AccessToken);
then
var result = await client.GetAsync("/v3/forgeapps/me");
and the above json is the result's content. I use the same access token in Postman and it works.
I would wrap up the endpoint, headers, and httpmethod in the HttpRequestMessage. Then send it and assign it to HttpResponseMessage.
var client = new HttpClient
{
BaseAddress = new Uri("https://developer.api.autodesk.com/da/us-east/")
};
//throw the endpoint and HttpMethod here. Could also be HttpMethod.Post/Put/Delete (for your future reference)
var request = new HttpRequestMessage(HttpMethod.Get, "v3/forgeapps/me");
//also maybe try throwing the headers in with the request instead of the client
request.Headers.Add(TokenType, AccessToken);
// send the request, assign to response
HttpResponseMessage response = await client.SendAsync(request);
//then, we can grab the data through the Content
string result = await response.Content.ReadAsStringAsync();

Can't set Content-Type header

I'm having trouble setting the Content-Type on HttpClient.
I followed along this question: How do you set the Content-Type header for an HttpClient request?
But still no luck.
String rcString = JsonConvert.SerializeObject(new RoadsmartChecks() { userguid = user_guid, coords = coordinates, radius = (radius * 100) + "" }, ROADSMART_JSON_FORMAT, JSONNET_SETTINGS);
HttpClient c = new HttpClient();
c.BaseAddress = new Uri(BASE_URL);
c.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); //Keeps returning false
c.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", hash_aes);
c.DefaultRequestHeaders.TryAddWithoutValidation("Roadsmart-app", Constant.APP_ID);
c.DefaultRequestHeaders.TryAddWithoutValidation("Roadsmart-user", user_guid);
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, BASE_URL + URL_CHECKS + "/fetch");
req.Content = new StringContent(rcString);
await c.SendAsync(req).ContinueWith(respTask =>
{
Debug.WriteLine("Response: {0}", respTask.Result);
});
I also tried by using the Flurl library, but it crashes when trying to add the 'Content-Type'.
misused header name content-type
So how can I force it so it really adds it?
Thanks in advance.
I think you should try this
req.Content = new StringContent(rcString, Encoding.UTF8, "application/json");
checkout this links :
How do you set the Content-Type header for an HttpClient request?
Edit
Remove this line c.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); and check
UPDATE: See new answer for non-default content types
With Flurl you shouldn't need to set Content-Type to application/json for methods like PostJsonAsync. This is the default content type in this case and it will get set for you.
The latest and greatest answer to this with Flurl is to upgrade. 2.0 introduces several enhancements in the headers dept:
They're no longer validated. Flurl now uses TryAddWithoutValidation under the hood, so you'll never get the "misused header name" error with the WithHeader(s) methods. (I always found that validation behavior to be a bit overprotective.)
In a fluent call they're set at the individual request level rather than the FlurlClient level, so you won't run into concurrency issues when reusing the client.
Since hyphens are common in header names but not allowed in C# identifiers, there's a new convention where underscores are converted to hyphens so you don't have to give up object notation when specifying multiple:
url.WithHeaders(new { Content_Type = "foo", ... }

Categories

Resources