我又踩坑了!如何為HttpClient請求設置Content-Type標頭?
本文轉載自微信公眾號「Dotnet Plus」,作者小碼甲 。轉載本文請聯系Dotnet Plus公眾號。
最近在重構認證代碼,認證過程相當常規:
- 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. 爬坑
--- | 信息 | 舉例 | .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") );