新項目上線, 發現一個奇怪的BUG
, 請求接口有很小的機率返回400 Bad Request
,拿到日誌記錄的請求的參數於POSTMAN
中測試請求接口, 發現可以正常響應.json
400 Bad Request
, 排除接口故障問題.{
"hello":"world"
}複製代碼
接口可以正常響應業務數據.瀏覽器
{
"hello":"world",
"kw":"我是八阿哥"
}複製代碼
則接口返回400
錯誤,接口的請求方式均爲post json
,因而開始review
代碼.發如今發送請求時設置了Content-Length
,在含中文字符的狀況下接口均返回400
,定位到緣由.請求的僞代碼以下bash
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
首部是爲了可以檢測出服務器崩潰而致使的報文截尾,並對共享持久鏈接的多個報文進行正確分段.服務器
其次,爲何含有中文字符的請求參數返回400
,由於Content-Length
是計算請求參數的字節數,而非字符數.而JSON.stringify(param).length
返回的是字符數.含中文字符的狀況下app
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')
post