理论
它们有不同的用途:
练习
尝试在请求中设置Content-Type
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add("Content-Type", "application/json");
这将产生以下异常:
System.InvalidOperationException:“使用不当的标头名称。确保请求标头与HttpRequestMessage一起使用,响应标头与HttpResponseMessage一起使用,内容标头与HttpContent对象一起使用。”
尝试在内容上设置Accept
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add("Accept","application/json");
这将产生以下异常:
System.InvalidOperationException:'错误的标头名称。请确保使用 HttpRequestMessage 处理请求标头,使用 HttpResponseMessage 处理响应标头,使用 HttpContent 对象处理内容标头。'
尝试在两个位置设置相同的任意标头:
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);
这不会产生任何异常。
哪个值被传递到下游?
为了能够回答这个问题,我将使用WireMock.Net nuget包来运行一个模拟服务器。
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));
- 我在这里定义了一个模拟服务器,它监听9000端口
- 它在路由
/
上有一个单一的终端点,并且预期是一个POST
请求
- 根据
headerKey
的值,它可能会响应
- 要么是
From Request header
- 要么是
From Content header
如果我发送一个请求,在两个对象上都设置了相同的标头键,则会收到以下响应:
From Request header
顺序是否重要?
如果我交换头部键的赋值顺序会发生什么?
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, address);
request.Headers.Add(headerKey, requestHeaderValue);
var content = new StringContent("Test", Encoding.UTF8);
content.Headers.Add(headerKey, contentHeaderValue);
结果将是相同的:
从请求头
。
为了完整起见,这里是完整的源代码:
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);
}
更新 #1 我的示例中发现了一个错误
我刚刚意识到我忘记使用 HttpRequestMessage
的 Content
属性:
var request = new HttpRequestMessage(HttpMethod.Post, address) { Content = content };
- 如果我们在两个位置都设置相同的标题,那么响应将是:
{"Status":"No matching mapping found"}
- 如果我们只在内容中设置此标题,则响应将为:
From Content header
- 如果我们只在请求中设置此标题,则响应将为:
From Request header
那么,为什么会收到这个No matching mapping found
?原因是因为在这种情况下发送了两个值,并且没有为该情况注册路由。
为了证明这个理论,让我们编写一些评估代码:
var logs = server.FindLogEntries(Request.Create().WithPath(route).UsingPost());
Console.WriteLine(logs.First().RequestMessage.Headers[headerKey].Count);
- 当我们从服务器收到响应后,可以通过
FindLogEntries
请求日志
- 在日志条目上,我们可以访问请求消息,因此可以仔细检查标头
- 如您所见,
A
标头包含两个值B
和C