我又踩坑了!如何為HttpClient請(qǐng)求設(shè)置Content-Type標(biāo)頭?
本文轉(zhuǎn)載自微信公眾號(hào)「Dotnet Plus」,作者小碼甲 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Dotnet Plus公眾號(hào)。
最近在重構(gòu)認(rèn)證代碼,認(rèn)證過(guò)程相當(dāng)常規(guī):
- 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"}
平臺(tái)顯示 :簽名校驗(yàn)失敗, 排查到平臺(tái)收到的Post Payload并非預(yù)期,閱讀本文,解鎖正確使用Content-Type標(biāo)頭的姿勢(shì)。
1. 入坑
下面是構(gòu)造HttpClient對(duì)象、發(fā)起請(qǐng)求的代碼:
- // 初始化HttpClientFactory
- context.Services.AddHttpClient("platform", c =>
- {
- c.BaseAddress = new Uri("https://alpha-engage.demohost.com/");
- c.DefaultRequestHeaders.Accept
- .Add(new MediaTypeWithQualityHeaderValue("application/json"));
- })...
- // 產(chǎn)生命名HttpClient,發(fā)起請(qǐng)求
- 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) );
平臺(tái)日志顯示,收到的請(qǐng)求payload:
- {\"token\":\"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74\"}
額,平臺(tái)收到的JSON數(shù)據(jù)被轉(zhuǎn)碼了,沒(méi)有識(shí)別出JSON?
明眼人一看,HttpClient請(qǐng)求沒(méi)有設(shè)置Content-Type,接收端沒(méi)有識(shí)別出JSON 格式的payload , 進(jìn)行了轉(zhuǎn)碼,生成了錯(cuò)誤簽名。
① Content-Type是一個(gè)Entity Header,指示資源的mediaType ,可用在請(qǐng)求/響應(yīng)中
② 代碼中new StringContent(req.ReqPayload.ToString(),Encoding.UTF8) 沒(méi)有指定mediaType參數(shù),故函數(shù)會(huì)使用text/plain默認(rèn)值
------------------------------------------
當(dāng)我嘗試添加Content-Type時(shí)(下面黃色背景行代碼):
- 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");
- })
此時(shí)拋出以下異常:
- 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. 爬坑
--- | 信息 | 舉例 | .NET類(lèi)型 |
---|---|---|---|
General Header | 可同時(shí)作用在請(qǐng)求/響應(yīng)中,但是與傳輸數(shù)據(jù)無(wú)關(guān) | Upgrade、Connection | --- |
Request Header | 將要獲取的資源或客戶(hù)端本身的信息 | Accept、 Authorization |
HttpRequestHeaders |
Response Header | 響應(yīng)信息 | Location、ETag | HttpResponseHeaders |
Entity Header |
實(shí)體Body額外的信息 | Content-Length、 Connection |
HttpContentHeaders |
Content-Type屬于Entity Header的一種,對(duì)應(yīng).NET類(lèi)型 HttpContent Header;
雖然Entity Header不是請(qǐng)求標(biāo)頭也不是響應(yīng)標(biāo)頭,它們還是會(huì)包含在請(qǐng)求/響應(yīng)標(biāo)頭術(shù)語(yǔ)中(此說(shuō)法來(lái)自官方)。
所以我們?cè)贑hrome DevTools沒(méi)有看到Entity Headers分組, 卻常在請(qǐng)求/響應(yīng)標(biāo)頭中看到Content-Type標(biāo)頭。
回到上面的異常,.NET 嚴(yán)格區(qū)分四種標(biāo)頭,所以c.DefaultRequestHeaders.Add("content-type", "application/json") 嘗試將content-type添加到請(qǐng)求頭,姿勢(shì)不正確,.NET提示InvalidOperationException。
3. 填坑
給這個(gè)常規(guī)的Post請(qǐng)求設(shè)置正確的Content-Type標(biāo)頭。
方法① 對(duì)HttpRequestMessage對(duì)象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)
方法② 寫(xiě)入HttpContent時(shí)傳入媒體類(lèi)型
StringContent某個(gè)重載構(gòu)造函數(shù) : 參數(shù)3 可直接設(shè)置media type,
- var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8,"application/json") );