作者 | 小码甲
来源 | Dotnet Plus (ID :nodotnet)
头图 | CSDN 下载自东方IC
最近在重构认证代码,认证过程相当常规:
POST /open-api/v1/user-info?client_id&timstamp&rd=12345&sign=***&method=hmac
content-type: application/json
payload: { "token":"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74"}![]()
平台显示 :签名校验失败, 排查到平台收到的 Post Payload 并非预期,阅读本文,解锁正确使用 Content-Type 标头的姿势。
入坑
下面是构造 HttpClient 对象、发起请求的代码:
// 初始化HttpClientFactory
context.Services.AddHttpClient("platform", c =>
{
c.BaseAddress = new Uri("https://alpha-engage.demohost.com/");
c.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
})...
// 产生命名HttpClient,发起请求
var client = _clientFactory.CreateClient("platform");
var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8) );
平台日志显示,收到的请求 payload:
{\"token\":\"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74\"}
额,平台收到的 JSON 数据被转码了,没有识别出 JSON?
明眼人一看,HttpClient 请求没有设置Content-Type,接收端没有识别出JSON 格式的 payload , 进行了转码,生成了错误签名。
① Content-Type是一个Entity Header,指示资源的mediaType ,可用在请求/响应中 ② 代码中new StringContent(req.ReqPayload.ToString(),Encoding.UTF8) 没有指定mediaType参数,故函数会使用text/plain默认值
当我尝试添加Content-Type时(下面黄色背景行代码):
context.Services.AddHttpClient("platform", c =>
{
c.BaseAddress = new Uri("https://alpha-engage.demohost.com/");
c.DefaultRequestHeaders.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
c.DefaultRequestHeaders.Add("content-type", "application/json");
})
此时抛出以下异常:
InvalidOperationException: Misused header name. Make sure request headers are used with
HttpRequestMessage, response headers with HttpResponseMessage, and
content headers with HttpContent objects.
爬坑
官方资料显示:HTTP Headers 被分为如下四类:
--- 信息 举例
.NET类型 General Header 可同时作用在请求/响应中,但是与传输数据无关 Upgrade、Connection --- Request Header 将要获取的资源或客户端本身的信息 Accept、
Authorization HttpRequestHeaders Response Header 响应信息 Location、ETag HttpResponseHeaders Entity
Header 实体Body额外的信息 Content-Length、
Connection HttpContentHeaders
Content-Type 属于 Entity Header 的一种,对应.NET 类型 HttpContent Header;
虽然Entity Header不是请求标头也不是响应标头,它们还是会包含在请求/响应标头术语中(此说法来自官方)。
所以我们在 Chrome DevTools 没有看到 Entity Headers 分组, 却常在请求/响应标头中看到 Content-Type 标头。
回到上面的异常,.NET 严格区分四种标头,所以c.DefaultRequestHeaders.Add("content-type", "application/json")尝试将 content-type 添加到请求头,姿势不正确,.NET 提示InvalidOperationException。
填坑
给这个常规的 Post 请求设置正确的 Content-Type 标头。
方法①对 HttpRequestMessage 对象 Content 属性添加 Header
using (var request = new HttpRequestMessage())
{
request.Method = new HttpMethod(method);
request.RequestUri = new Uri(url);
request.Content = new StringContent(payload);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.SendAsync(request);
return response;
}
使用 HttpClient.SendAsync(request)
方法②写入 HttpContent 时传入媒体类型
StringContent某个重载构造函数 : 参数3 可直接设置media type,
var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8,"application/json") );
干货旁白
小编对于 Http 协议有知识漏洞,搬砖时一直关注 Chrome DevTools,忽略了还有 Entity Header 一说。
Content-Type 这个实体标头,会出现了请求/响应标头,指示资源的媒体类型。
.NTE 针对4种 HTTP Header 强化了区别,在实际开发中要区别使用。
https://tools.ietf.org/html/rfc2616#page-41
https://developer.mozilla.org/en-US/docs/Glossary/Entity_header
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.