我又踩坑了!如何為HttpClient請求設置Content-Type?


1. 坑位

最近在重構認證代碼,認證過程相當常規:

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標頭的姿勢。

2. 步步為營

下面是構造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,接收端沒有識別出payload是JSON,接收時進行了轉碼,生成了錯誤簽名。

① Content-Type是一個Entity Header,指示資源的media type ,可用在請求或者響應中。
② 以上代碼中new StringContent(req.ReqPayload.ToString(),Encoding.UTF8)指定了Encoding=UTF-8,卻沒有指定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啊?

3. 爬坑

官方資料顯示: HTTP Headers被分為如下四類:

--- 信息 舉例 對應.NET類型
General Header 可同時作用在請求/響應中,但是與傳輸數據無關 Upgrade、Connection ---
Request Header 將要獲取的資源或客戶端本身的信息 Accept、Authorization System.Net.Http.Headers.HttpRequestHeaders
Response Header 響應信息 Location、ETag System.Net.Http.Headers.HttpResponseHeaders
Entity Header 實體Body額外的信息 Content-Length、Content-Type System.Net.Http.Headers.HttpContentHeaders

Content-Type屬於Entity Header中的一種。

即便Entity Header既不是請求標頭也不是響應標頭,它們還是會包含在請求/響應標頭術語中(此說法來自官方)。

所以我們在Chrome DevTools沒有看到Entity Headers分組,卻常在請求/響應標頭中看到Content-Type標頭。

回到上面的異常,.NET 嚴格區分四種標頭,所以我上面
c.DefaultRequestHeaders.Add("content-type", "application/json");嘗試在請求頭添加Content-Type標頭, 姿勢錯誤, .NET會提示InvalidOperationException異常。

4. 填坑

給這個常規的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)

② 對StringContent重載構造函數傳參

StringContent某個重載構造函數可直接傳參,設置media type

var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8,"application/json") );

干貨旁白

  1. 小編對於Http協議有知識漏洞,搬磚時一直關注Chrome DevTools,忽略了還有Entity Header一說。
  2. Content-Type 這個實體標頭,會出現了請求/響應標頭,指示資源的媒體類型。
  3. .NTE針對4種HTTP Header強化了區別,在實際開發中要靈活使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM