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

2021-12-09 CSDN

來源 | Dotnet Plus(ID:nodotnet)
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. 

納尼,HttpContent Headers 是啥?Chrome dev tools 顯示只有兩種Header 啊?

 

爬坑

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

---信息舉例
.NET類型General Header可同時作用在請求/響應中,但是與傳輸數據無關Upgrade、Connection---Request Header將要獲取的資源或客戶端本身的信息Accept、
AuthorizationHttpRequestHeadersResponse Header響應信息Location、ETagHttpResponseHeadersEntity
Header實體Body額外的信息Content-Length、
ConnectionHttpContentHeadersContent-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 強化了區別,在實際開發中要區別使用。

更多精彩推薦

☞300億美元,AMD為什麼要買Xilinx?

☞1024程式設計師節開源技術英雄會,參會「英雄榜」發榜

☞如何應對雲原生之旅中的安全挑戰?

☞採摘工人月薪十萬卻無人應聘,英澳農場求助 AI

☞還不懂Redis?看完這個故事就明白了!

☞區塊鏈+生鮮:杜絕「偷梁換柱」和「以次充好」

相關焦點

  • HTTP請求中,幾種常見的Content-Type類型解析
    (例如,指定HEAD方法送到接收方的實體介質類型,或GET方法發送的請求介質類型,表示後面的文檔屬於什麼MIME類型。)在響應中,Content-Type標頭告訴客戶端實際返回的內容的內容類型。瀏覽器會在某些情況下進行MIME嗅探,並不一定遵循此標題的值; 為了防止這種行為,可以將標題 X-Content-Type-Options 設置為 nosniff。
  • get、post 請求中常見 content-type 請求頭以及 nodeJs 解析請求參數
    form 標籤的 enctype 屬性的定義和用法enctype 屬性規定在發送到伺服器之前應該如何對表單數據進行編碼
  • 【每日一題】說說開發中常用的幾種 Content-Type?
    Content-Type 的定義HTTP 的實體首部欄位,用於說明請求或返回的消息主體是何種方式編碼,在 request header(請求頭) 和 response header(響應頭) 裡存在。幾種常見的類型:application/x-www-form-urlencoded瀏覽器的原生 form 表單,如果不設置屬性,那麼最終就會以application/x-www-form-urlencoded方式提交數據
  • 童鞋,[HttpClient發送文件的技術實踐]請查收
    之前我寫了一個《ABP小試牛刀之上傳文件》,主要體現的是服務端,上傳文件的動作是由前端小姐姐完成的, 我還真沒有用HttpClient編程方式發送過文件。不過HttpClient的動作遵守Web協議,盲猜httpclient按照前端multipart/form-data媒體類型發送文件應該也是可行的。
  • 如何使用Django開發OpenRASP報警接收Web應用
    VirtualizationVirtualization,或者叫做containerized runtime protection,它創建一個應用程式副本,並通過使用規則來控制應用程式該如何被保護,同時控制應用程式在副本上的運行時行為。RASP監視和學習應用程式代碼路徑、邏輯構造、參數化和生成的輸出等,然後應用於應用程式請求。
  • UNRAID進階避坑指南(篇二):UNRAID進階配置
    這樣UNRAID重啟之後就會等待5分鐘就會執行一次掛載NAS共享文件服務的命令,這個時候去看Unassigned Devices插件的狀態,對應的共享文件服務share type前已經亮起了綠色小標,就代表共享文件服務已經掛載成功如何實現docker加速如果不配置docker鏡像加速的話,docker鏡像的拉取速度會慢得令人髮指。
  • Prometheus使用總結:我踩過的那些坑
    我在工作中也比較深入地使用過 Prometheus,最大的感受就是它非常容易維護,突出一個簡單省心成本低。當然,這當中也免不了踩過一些坑,下面就總結一下。假如你沒有用過 Prometheus,建議先看一遍官方文檔:https://prometheus.io/docs/introduction/overview/。
  • Mitmproxy 快速帶你入坑!
    最近在搞 App 爬蟲,不過萬事入門難,我得自己研究研究 Mitmproxy 雖然不知道到底是不是那麼強啦,就是得要會用,所以我就親自入坑,並把自己的一下觀點比較記錄下來,畢竟好記性不然爛筆頭嘛。**例如:截獲對該瀏覽器的請求,將返回數據為空並將真實的返回數據存到某一個資料庫中或者出現異常的時候發出郵件通知。
  • 數據挖掘:聊聊那些年你我踩過的「坑」
    今天就來談一談數據挖掘中常常被我們忽略的小問題(踩過的坑)。讓我們從下面這張圖開始吧! 圖一 從現實世界到模型世界(圖片來自網絡) 咳咳注意,本篇不是八卦文,在這裡我們要正經地討論一些小case。如圖所示,我們以左圖代表現實世界,右圖代表模型世界——對,數據挖掘的世界。
  • 經驗 | 在C++平臺上部署PyTorch模型流程+踩坑實錄
    主要原因可歸為一下兩點1.__init__() # This type cannot be inferred and must be specified self.my_dict = {} # The attribute type here is inferred to be `int` self.my_int = 20
  • 踩坑|NVIDIA驅動安裝
    用GPU並行的話就報下面的錯:研究了半天,發現是cudnn沒裝好,於是我開始裝cudnn。裝的時候發現cuda沒有安裝,於是又開始安裝cuda,裝著裝著又報錯了,看了報錯的log文件,發現是驅動的問題:「The driver installation is unable to locate the kernel source. 」好吧,那就重新裝驅動吧。
  • virtualbox centos7 nat+host-only方式聯網踩坑總結
    1、問題背景按照以往習慣的設置我採用橋接模式進行上網,但現在面臨的需求場景是這樣:要求centos虛擬機可以yum install、docker拉網際網路鏡像,因此需要訪問網際網路。本地xshell可以ssh訪問cenots虛擬機。
  • Java 日常開發的 21 個坑
    日期YYYY格式設置的坑日常開發,經常需要對日期格式化,但是呢,年份設置為YYYY大寫的時候,是有坑的哦。= -1) {                content += new String(chars, 0, count);            }        }        System.out.println(content);    }}5.