How can I set response header in httpclient in C#? - c#

I am getting response in text/html format.How can I set response header so that I get response in application/json format.
Code:
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = builder.Uri,
Headers = {
{ HttpResponseHeader.ContentType.ToString(), "application/json" },
{ "Auth", "###################################################" }
}
};
var httpResponse = await _httpClient.SendAsync(httpRequestMessage);

You don't. Content-Type header
In responses, a Content-Type header tells the client what the content type of the returned content actually is.
The server decides the content type of the response and the client uses it to handle accordingly. There is nothing you can set in your request to do that out of the box.
If you are handling the response creation, you could create a custom header to indicate the type of the response and have the server side code handle it, but that's not usually how it should be done.

As stated by #Athanasios, you cannot specify the Content-Type returned by the server. But, there is one thing you can try : you can tell the server that you'd like to get a JSON format response.
Set the HTTP Header Accept to application/json
But, it's still up to the server in the end to decide what content will be returned

It seems that you are looking for Accept header. Try next:
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = builder.Uri,
Headers = {
{ "Accept", "application/json"},
{ "Auth", "###################################################" }
}
};

Related

What is the difference between request headers and content headers for HttpClient

Can anybody explain what is the difference between the request header and content header?
In this particular case I'm talking about UWP HttpClient object. First you create HttpClient, then you create HttpRequestMessage and then you assign, in my case HttpStreamContent to the Content property of the HttpRequest message. There is Headers property on the HttpRequestMessage and there is Headers property on the HttpStreamContent.
When should I use one or another?
Where exactly the headers will appear in one or another case?
Here is a code snippet to explain what I mean
using(var objProtocolFilter = new HttpBaseProtocolFilter()) {
objProtocolFilter.AllowUI = false;
objProtocolFilter.CacheControl.ReadBehavior = HttpCacheReadBehavior.NoCache;
objProtocolFilter.CacheControl.WriteBehavior = HttpCacheWriteBehavior.NoCache;
using(var objClient = new HttpClient(objProtocolFilter)) {
HttpMethod eMethod = Method switch {
HttpUploadMethod.Post => HttpMethod.Post,
HttpUploadMethod.Put => HttpMethod.Put,
_ => throw new ValueOutOfRangeException(nameof(Method))
};
using(var objRequest = new HttpRequestMessage(eMethod, RemoteUri)) {
_Headers.Cast<string>().Execute(item => objRequest.Headers.TryAppendWithoutValidation(item, _Headers[item]));
objRequest.Content = new HttpStreamContent(objInputStream.AsInputStream());
_Headers.Cast<string>().Execute(item => objRequest.Content.Headers.TryAppendWithoutValidation(item, _Headers[item]));
objRequest.Content.Headers.ContentLength = (ulong)objInputStream.Length;
}
}
}
Here I just add the same list of headers to HttpRequestMessage and to HttStreamContent. I guess it's wrong unless those objects are smart enough to apply only permitted headers in one or the other case. So, which headers should go where? Are they interchangeable?
Theory
They serve different purpose:
HttpStreamContent.Headers returns an HttpContentHeaderCollection
You can set things like: Content-Disposition, Content-Range, Content-Length, Content-Type, etc.
In other words these are related to the request's body
HttpRequestMessage.Headers returns an HttpRequestHeaderCollection
You can set things like: Accept, Accept-Encoding, Authorization, Cookie, etc.
In other words these are related to the request itself.
+1: (HttpResponseMessage.Headers returns an HttpResponseHeaderCollection)
Practice
Try to set Content-Type on the Request
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add("Content-Type", "application/json");
This will produce the following exception:
System.InvalidOperationException: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
Try to set Accept on Content
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add("Accept","application/json");
This will produce the following exception:
System.InvalidOperationException: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
Try to set the same arbitrary header at both places:
const string headerKey = "A", requestHeaderValue = "B", contentHeaderValue = "C";
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add(headerKey, contentHeaderValue);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add(headerKey, requestHeaderValue);
This will not produce any exception.
Which value is passed to the downstream?
In order to be able answer this question I will use the WireMock.Net nuget package to run a mock server.
const string address = "http://localhost:9000", route = "/";
var server = WireMockServer.Start(new WireMockServerSettings { Urls = new[] { address } });
server
.Given(Request.Create()
.WithPath(route)
.WithHeader(headerKey, new ExactMatcher(requestHeaderValue))
.UsingPost())
.RespondWith(Response.Create()
.WithBody("From Request header")
.WithStatusCode(200));
server
.Given(Request.Create()
.WithPath(route)
.WithHeader(headerKey, new ExactMatcher(contentHeaderValue))
.UsingPost())
.RespondWith(Response
.Create()
.WithBody("From Content header")
.WithStatusCode(200));
Here I have defined a mock server which listens on the 9000 port
It has a single endpoint on the / route and it anticipates a POST request
Depending on the headerKey value it may respond
either with From Request header
or with From Content header
If I send a request where the same header key is set on both objects then I will receive the following response:
From Request header
Does ordering matter?
What if I switch the order of the header key assignments?
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add(headerKey, requestHeaderValue);
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add(headerKey, contentHeaderValue);
The result will be the same: From Request header.
For the sake of completeness here is the full source code:
static readonly HttpClient httpClient = new HttpClient();
static async Task Main(string[] args)
{
const string headerKey = "A", requestHeaderValue = "B", contentHeaderValue = "C";
const string address = "http://localhost:9000", route = "/";
var server = WireMockServer.Start(new WireMockServerSettings { Urls = new[] { address } });
server
.Given(Request.Create()
.WithPath(route)
.WithHeader(headerKey, new ExactMatcher(requestHeaderValue))
.UsingPost())
.RespondWith(Response.Create()
.WithBody("From Request header")
.WithStatusCode(200));
server
.Given(Request.Create()
.WithPath(route)
.WithHeader(headerKey, new ExactMatcher(contentHeaderValue))
.UsingPost())
.RespondWith(Response
.Create()
.WithBody("From Content header")
.WithStatusCode(200));
var request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add(headerKey, requestHeaderValue);
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add(headerKey, contentHeaderValue);
var response = await httpClient.SendAsync(request);
var headerSource = await response.Content.ReadAsStringAsync();
Console.WriteLine(headerSource);
}
UPDATE #1 Found a bug in my example
I've just realized that I forgot to use the Content property of the HttpRequestMessage:
var request = new HttpRequestMessage(HttpMethod.Post, address) { Content = content };
If we set the same header in both places then the response will be: {"Status":"No matching mapping found"}
If we set this header only on the content then the response will be: From Content header
If we set this header only on the request then the response will be: From Request header
So, why did we receive this No matching mapping found? The reason is that because both values are sent in this case and there is no registered route for that case.
To prove the theory let's write some assessment code:
var logs = server.FindLogEntries(Request.Create().WithPath(route).UsingPost());
Console.WriteLine(logs.First().RequestMessage.Headers[headerKey].Count);
After we have received the response from the server we can ask for the logs (via FindLogEntries)
On a log entry we can access the request message so, we can scrutinize the headers
As you can see the A header contains both values B and C

Unsupported media type in httpclient call c#

I'm a trying to post the following request but I am getting a "Unsupported Media Type" response. I am setting the Content-Type to application/json. Any help would be appreciated. And as per comment below, if i change content as 'new StringContent(JsonConvert.SerializeObject(root), Encoding.UTF8, "application/json")' then i get bad request response
string URL = "https://test.com/api/v2/orders/"; //please note it is dummy api endpoint
var client = new HttpClient();
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(URL),
Headers = {
{ HttpRequestHeader.Authorization.ToString(), "Bearer ABcdwenlfbl8HY0aGO9Z2NacFj1234" }, //please note it is dummy bearer token
{ HttpRequestHeader.Accept.ToString(), "application/json;indent=2" },
{ HttpRequestHeader.ContentType.ToString(), "application/json" }
},
//Content =new StringContent(JsonConvert.SerializeObject(root), Encoding.UTF8, "application/json")
Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(root))
};
var response = client.SendAsync(httpRequestMessage).Result;
With HttpClient, some headers are counted as request headers, and others are counted as content headers. I'm not sure why they made this distinction really, but the bottom line is that you have to add headers in the correct place.
In the case of Content-Type, this can be added as part of the StringContent constructor, or to the constructed StringContent object.
My approach is to use the constructor:
Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(root), System.Text.Encoding.UTF8, "application/json");
Or alternatively set it afterwards:
Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(root))
Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Note: If your issue still presents after making this change, then it's likely a server-side problem and you'll need to contact the maintainer of the API to ask what you're doing wrong.
I prefer using some third party wrappers like FluentClient
Note that you should not instance a new object for every request, O only did it for the sake of an example.
var client = new FluentClient("https://test.com/api/v2/orders/")
.PostAsync(URI)
.WithBody(root)
.WithBearerAuthentication("ABcdwenlfbl8HY0aGO9Z2NacFj1234");
var response = await client.AsResponse();

How to set content-md5 header in GET method using HttpClient?

I have the following code to set content-md5 in my GET method request using HttpClient
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("content-md5", "value");
I cannot use HttpRequestMessage content to set it because it's not a POST method. When using Postman it works like a charm but fails when using HttpClient.GetAsync.
Client request a hmac to the server as follows
{
"content_to_hash": "my content"
}
The server will give response like this
{
"content_md5": "88af7ceab9fdafb76xxxxx",
"date": "Sat, 02 May 2020 00:13:16 +0700",
"hmac_value": "WfHgFyT792IENmK8Mqz9LysmP8ftOP00qA="
}
Now I have to access a GET request using that hmac where it's the problem because I cannot set in httpClient GET request header.
Here's the image
From reading the HttpClient and related source code, there's no way you can get around this and add the header to the actual request object headers. There is an internal list of invalid headers, which includes any Content-* headers. It has to be on a content object.
Therefore, my suggest solution is to create your own content object:
public class NoContentMd5 : HttpContent
{
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return Task.CompletedTask;
}
protected override bool TryComputeLength(out long length)
{
length = 0;
return false;
}
public NoContentMd5(byte[] contentMd5)
{
this.Headers.ContentMD5 = contentMd5;
}
public NoContentMd5(string contentMd5)
{
this.Headers.TryAddWithoutValidation("Content-MD5", contentMd5);
}
}
This will add the Content-MD5 header with a value of your choosing, but the request won't contain a body.
The next problem you'll encounter is that you're trying to make a GET request with content, which isn't supported by the helper client.GetAsync(...) method. You'll have to make your own request object and use client.SendAsync(...) instead:
HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost/my/test/uri");
request.Content = new NoContentMd5("d41d8cd98f00b204e9800998ecf8427e ");
var result = await client.SendAsync(request);
Note that if you have your Content-MD5 hash as bytes, I've also added a constructor to NoContentMd5 for byte[] too.
The only potential issue with this is that it includes a Content-Length: 0 header. Hopefully that's OK with the API you're working with.
There's an alternative solution described in this answer to question with a similar issue. I'd argue against using it since is vulnerable to changes in the implementation details of HttpRequestHeaders (because it uses reflection, so if MS change the code, it might break) .
Aside from the fact that it's not considered good practice to send a body with GET request (see HTTP GET with request body), you can try this:
using (var content = new StringContent(string.Empty))
using (var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("http://localhost"),
Content = content
})
{
request.Headers.TryAddWithoutValidation("content-md5", "value");;
using (var response = await httpClient.SendAsync(request))
{
response.EnsureSuccessStatusCode();
}
}
UPDATE:
The proper way would be to set the ContentMD5 property on the HttpContentHeaders, for example:
content.Headers.ContentMD5 = Convert.FromBase64String(hashString);
But as you pointed out in the comments, trying to send content in a GET request causes an error.

C#: Sending parameter with Content-Type Header in HTTP

I am trying to send the HTTP post request using HTTP client.while setting my Content-Type header with StringContent method, I am getting the error:
the format "application/json;ty=2 "is incorrect
My code is as follows:
string result2 = JsonConvert.SerializeObject(values);
var content = new StringContent(result2, Encoding.UTF8, "application/json; ty=2");
var response = await client.PostAsync("http://127.0.0.1:8080/~/in-cse/in-name", content);
var response_string = await response.Content.ReadAsStringAsync();
But in my HTTP Post format I have to include ty=2 parameter along with application/json. I have tested this with Postmanand it worked perfectly. How can I achieve the same thing here. I also even found you can add parameter to content type in the following format:
content := "Content-Type" ":" type "/" subtype *(";" parameter)
then why is it not working for me.
**EDIT:**even tried this:
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:8080/~/in-cse/in-name");
request.Content = new StringContent(result2, Encoding.UTF8, "application/json;ty=2");
client.SendAsync(request).ContinueWith(resposetask => { Console.WriteLine("response:{0}", resposetask.Result); });
same error i am getting
You will have to use TryAddWithoutValidation in DefaultRequestHeaders in HttpClient like following:
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json;ty=2");

Invalid operation during the http request

I'm writing a windows forms app where user have to enter email address and I need to check if it's valid.
I have found Mashape API but I get an exception;
"An unhandled exception of type 'System.InvalidOperationException' occurred in System.Net.Http.dll" error in Unirest library.
Error occurs in line with msg.Headers.Add(header.Key, header.Value);
Values are:
header.Key = "Content-type"
header.Value = "application/json"
Debugger says:
"Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
I can't find any solution, does anyone has any idea how to fix it?
Task<HttpResponse<EWSemail>> response = Unirest.post("https://email.p.mashape.com/")
.header("X-Mashape-Key", myKey)
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.body("{\"email\":\"" + tbEmail.Text + "\"}")
.asJsonAsync<EWSemail>();
Unirest library:
private static Task<HttpResponseMessage> RequestHelper(HttpRequest request)
{
if (!request.Headers.ContainsKey("user-agent"))
{
request.Headers.Add("user-agent", USER_AGENT);
}
var client = new HttpClient();
var msg = new HttpRequestMessage(request.HttpMethod, request.URL);
foreach (var header in request.Headers)
{
msg.Headers.Add(header.Key, header.Value);
} // ^^"Content-Type" ^^ "application/json"
if (request.Body.Any())
{
msg.Content = request.Body;
}
return client.SendAsync(msg);
}
The error message says:
"Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
Content-Type, as the name would imply, is a content header. Therefore, set it on msg.Content.Headers, not msg.Headers.
If I remember correctly, you can set it directly via msg.Content.Headers.ContentType = new MediaHeaderTypeValue("application/json");
OK, it doesn't work because syntax that Mashape provide is bad. In this case header.Key has to be "ContentType" (Reference: https://msdn.microsoft.com/en-us/library/system.net.httprequestheader%28v=vs.110%29.aspx).
I'm sorry for my rubbish post and thank you #codran, your answer helped me to find this answer.

Categories

Resources