新項目上線, 發現一個奇怪的BUG
, 請求接口有很小的機率返回400 Bad Request
,拿到日誌記錄的請求的參數於POSTMAN
中測試請求接口, 發現可以正常響應.json
400 Bad Request
, 排除接口故障問題.{ "hello":"world" }
接口可以正常響應業務數據.瀏覽器
{ "hello":"world", "kw":"我是八阿哥" }
則接口返回400
錯誤,接口的請求方式均爲post json
,因而開始review
代碼.發如今發送請求時設置了Content-Length
,在含中文字符的狀況下接口均返回400
,定位到緣由.請求的僞代碼以下服務器
let param = { "hello":"world", "kw":"我是八阿哥" } let _options = { headers: { 'Content-Type': 'application/json', 'Content-Length': JSON.stringify(param).length }, url: url, method: 'POST', json: true, time: true, timeout: 5 * 1000, body: param } return new Promise((resolve,reject)=>{ request(_options,(error, response, body)=>{ ///XXXX ]) })
首先, 來講說什麼是Content-Length
,在http
的協議中Content-Length
首部告訴瀏覽器報文中實體主體的大小。這個大小是包含了內容編碼的,好比對文件進行了gzip
壓縮,Content-Length
就是壓縮後的大小(這點對咱們編寫服務器很是重要)。除非使用了分塊編碼,不然Content-Length
首部就是帶有實體主體的報文必須使用的。使用Content-Length
首部是爲了可以檢測出服務器崩潰而致使的報文截尾,並對共享持久鏈接的多個報文進行正確分段.app
其次,爲何含有中文字符的請求參數返回400
,由於Content-Length
是計算請求參數的字節數,而非字符數.而JSON.stringify(param).length
返回的是字符數.含中文字符的狀況下post
console.log('八阿哥'.length) //3, 即3個字符
console.log(Buffer.byteLength('八阿哥', 'utf8')); //9, utf-8編碼下,一個漢字是3字節存儲的
致使接口層拿到的Content-Length
小於真實的字節長度, 於是沒法正確的解析數據, 從而返回400 Bad Request
.所以須要將設置Content-Length
的長度改成Buffer.byteLength(JSON.stringify(param),'utf8')
測試