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?
They serve different purpose:
Content-Disposition, Content-Range, Content-Length, Content-Type, etc.Accept, Accept-Encoding, Authorization, Cookie, etc.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.'
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.'
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.
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));
/ route and it anticipates a POST requestheaderKey value it may respond
From Request headerFrom Content headerIf I send a request where the same header key is set on both objects then I will receive the following response:
From Request header
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 };
{"Status":"No matching mapping found"}From Content headerFrom Request headerSo, 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);
FindLogEntries)A header contains both values B and CIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With