最近在重構認證代碼,認證程序相當常規:
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標頭的姿勢,
1. 入坑
下面是構造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.
納尼,HttpContent Headers是啥?Chrome dev tools顯示只有兩種Header啊?
2. 爬坑
官方資料顯示: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,
3. 填坑
給這個常規的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") );
4.干貨旁白
-
小編對于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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/144752.html
標籤:其他
上一篇:發表博客
