HttpRequestMessage 的 Content-Type 标头中的奇怪行为

Posted

技术标签:

【中文标题】HttpRequestMessage 的 Content-Type 标头中的奇怪行为【英文标题】:Strange behavior in HttpRequestMessage's Content-Type header 【发布时间】:2019-03-12 08:54:08 【问题描述】:

我尝试从 C# (.NET Core 2.2.104) 向外部 Web 服务发出带有 application/json 的 HTTP POST 请求。

我已经阅读了 SO 中的所有类似问题并编写了以下代码:

            SignXmlRequestDto requestBody = new SignXmlRequestDto(p12, model.SignCertPin, model.Data);
            string json = JsonConvert.SerializeObject(requestBody);

            var httpRequestMessage = new HttpRequestMessage
            
                Method = HttpMethod.Post,
                RequestUri = ncanNodeUrl,
                Headers =
            
                 HttpRequestHeader.ContentType.ToString(), "application/json" 
            ,
                Content = new StringContent(JsonConvert.SerializeObject(json))
            ;

            var response = await httpClient.SendAsync(httpRequestMessage);

            string responseString = await response.Content.ReadAsStringAsync();

我收到来自服务的错误,它说:“无效的标头 Content-Type。请将 Content-Type 设置为 application/json”。这里有趣的是,如果我模拟 Postman 的这个请求,那么一切正常,我得到了成功的响应。

更新:正如@Kristóf Tóth 所建议的,我将代码修改为:

            var httpRequestMessage = new HttpRequestMessage
            
                Method = HttpMethod.Post,
                RequestUri = ncanNodeUrl,
                Content = new StringContent(json, Encoding.UTF8, "application/json")
            ;

            var response = await httpClient.SendAsync(httpRequestMessage);

            string responseString = await response.Content.ReadAsStringAsync();

但它仍然给我同样的错误信息。

【问题讨论】:

HttpRequestHeader.ContentType.ToString() ?? @PanagiotisKanavos,在这个答案中建议***.com/a/53451542 没有被接受或反对,这是有原因的。 Content-Type 是初学者的内容标题。在 StringContent 构造函数中传递内容类型,或者在内容对象上设置它 使用 Fiddler 或其他调试代理并捕获实际发送的内容。在 StringContent 构造函数中传递内容类型是每个人发布 JSON 请求的方式。邮递员请求是什么样的?您的查询有何不同? 【参考方案1】:

Content-Type 是一个 content 标头。它应该设置在内容上,而不是请求本身。这可以使用StringContent(string,Encoding,string) 构造函数来完成:

Content = new StringContent(JsonConvert.SerializeObject(json),Encoding.UTF8, "application/json")

或通过设置 StringContent 的 Headers.ContentType 属性:

var content=new StringContent(JsonConvert.SerializeObject(json));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

【讨论】:

哇,第二个选项解决了我的问题 - 设置 StringContent 的 Headers.ContentType 属性,它可以工作。现在我想知道,为什么第一个不起作用,而第二个起作用?我认为两者都做同样的动作...... @IskanderRaimbayev 使用 Fiddler 并检查有什么不同。 Postman 可以帮助您手动创建 HTTP 请求,但无法显示实际发送或接收的内容。 Fiddler 捕获两者 @IskanderRaimbayev 您使用的是哪个 .NET 版本?您是否通过 NuGet 包添加了 HttpClient?在构造函数中传递内容类型是很常见的,所以人们会注意到。 也许虽然存在一些模糊的错误组合,例如在特定的 HttpClient 版本或 .NET Core 运行时中,但这仍然不太可能 我使用 .NET Core 2.2.104,并首先尝试使用此答案中的代码:***.com/a/10679340/6159500。【参考方案2】:

这可能是编码问题。你应该使用 JsonContent 而不是 StringContent 或者你可以做类似的事情:

// Serialize into JSON String
var stringPayload = JsonConvert.SerializeObject(payload);

// Wrap JSON StringContent which then can be used by the HttpClient class
var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");

【讨论】:

这不是编码问题 - 该错误抱怨 Content-Type 标头,没有在内容中设置。你的回答正是这样做的 好像解决不了问题,还是报同样的错误。 你尝试了什么?这就是发出 HTTP POST 请求的方式。如果这不起作用,人们会在 4 年前 HttpClient 出现时注意到

以上是关于HttpRequestMessage 的 Content-Type 标头中的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

如何发送带有 HttpRequestMessage 的证书?

HttpClient 标头与 HttpRequestMessage 标头

HttpRequestMessage.CreateResponse 抛出 ***Exception

重新发送 HttpRequestMessage - 异常

HttpRequestMessage 多个自定义标头相互覆盖

如何在 .NET Core 中模拟/存根 HttpRequestMessage?