問(wèn)題描述
客戶(hù)端發(fā)起的HTTP POST請(qǐng)求, 到達(dá)服務(wù)器后請(qǐng)求方法莫名其妙變成了GET請(qǐng)求, 導(dǎo)致客戶(hù)端收到的是404烤宙。
問(wèn)題定位
首先檢查代碼, 再三確認(rèn)并且在測(cè)試環(huán)境上驗(yàn)證后確保代碼沒(méi)問(wèn)題嗜愈。
-
因?yàn)槭巧a(chǎn)環(huán)境出現(xiàn)的問(wèn)題, 便排查測(cè)試環(huán)境和生產(chǎn)環(huán)境的區(qū)別:
- 兩者客戶(hù)端代碼相同
- 兩者服務(wù)端代碼相同
- 生產(chǎn)環(huán)境用Nginx做了域名轉(zhuǎn)發(fā), 而測(cè)試環(huán)境直接使用的IP訪(fǎng)問(wèn), 未使用代理
于是嘗試將問(wèn)題定位在Nginx轉(zhuǎn)發(fā)上, 馬上重現(xiàn)問(wèn)題, 并且觀察Nginx日志, 發(fā)現(xiàn)轉(zhuǎn)發(fā)時(shí)出現(xiàn)了如下日志:
[27/Jul/2020:15:53:01 +0800] [訪(fǎng)問(wèn)的URL] "POST /api/v/game/query HTTP/1.1" 301 185 "-" "PostmanRuntime/7.25.0" "-"
[27/Jul/2020:15:53:01 +0800] [訪(fǎng)問(wèn)的URL] "GET /api/v/game/query HTTP/1.1" 404 18 "http://訪(fǎng)問(wèn)的URL/api/v/game/query" "PostmanRuntime/7.25.0" "-"
從上面的日志可以看出:
- Nginx收到的請(qǐng)求確實(shí)為POST請(qǐng)求, 客戶(hù)端請(qǐng)求沒(méi)有問(wèn)題。
- 從Nginx到服務(wù)端請(qǐng)求發(fā)生了變化: POST被轉(zhuǎn)換成了GET請(qǐng)求, 由此服務(wù)端返回404。
由此可以確定: 問(wèn)題的出現(xiàn)是因?yàn)镹ginx的轉(zhuǎn)發(fā)使HTTP方法類(lèi)型發(fā)生了變化颠焦。
問(wèn)題原因
那么Nginx為什么會(huì)把POST請(qǐng)求轉(zhuǎn)換成GET請(qǐng)求呢通今?注意上面的第一行日志中有301的字樣, 301狀態(tài)碼的意思是: 資源位置永久改變, 需要重定向, 通常用于將HTTP請(qǐng)求遷移到HTTPS。
到這里, 回頭看看Nginx的配置文件, 文件中配置了listen 443 ssl
, ssl_certificate
, ssl_certificate_key
等參數(shù), 即Nginx配置的是HTTPS服務(wù), 所有請(qǐng)求將以HTTPS訪(fǎng)問(wèn), 對(duì)于HTTP請(qǐng)求, 將會(huì)被以HTTPS的形式重定向娇妓。
再看看客戶(hù)端發(fā)起請(qǐng)求的URL, 確實(shí)是HTTP請(qǐng)求, 所以觸發(fā)了重定向, 也就導(dǎo)致了問(wèn)題的產(chǎn)生像鸡。
即使通過(guò)Nginx將HTTP轉(zhuǎn)換成了HTTPS, 這里也并沒(méi)有解釋為什么POST會(huì)變成GET請(qǐng)求, 這里就需要祭出著名的《圖解HTTP》中關(guān)于狀態(tài)碼的解釋了:
書(shū)中關(guān)于3xx
狀態(tài)碼的解釋:
1. 301-Moved Permanently(永久性重定向), 該狀態(tài)碼表示請(qǐng)求的資源已經(jīng)被分配了新的URI, 以后應(yīng)使用資源現(xiàn)在所指的URI, 也就是說(shuō)如果已經(jīng)把資源對(duì)應(yīng)的URI保存為書(shū)簽了, 這時(shí)應(yīng)該按Location首部字段提示的URI重新保存。
2. 302-Found(臨時(shí)重定向), 該狀態(tài)碼表示請(qǐng)求的資源已經(jīng)被分配了新的URI, 希望用戶(hù)(本次)能使用新的URI訪(fǎng)問(wèn)哈恰。和301不同的是, 302不是永久移動(dòng), 只是臨時(shí)性質(zhì)的, 也就是已移動(dòng)的資源對(duì)應(yīng)的URI將來(lái)還有可能發(fā)生改變, 如果URI被保存為書(shū)簽, 用戶(hù)不需要更新書(shū)簽只估。
3. 303-See Other(存在另一個(gè)URI), 該狀態(tài)碼表示請(qǐng)求的資源存在著另一個(gè)URI, 應(yīng)使用GET方法定向獲取請(qǐng)求的資源。303和302功能相同, 但303明確表示客戶(hù)端應(yīng)當(dāng)采用GET方法獲取資源着绷。比如, 當(dāng)使用POST方法訪(fǎng)問(wèn)時(shí), 其執(zhí)行后的處理結(jié)果是希望客戶(hù)端能以GET方法重定向到另一個(gè)URI上去, 則返回303狀態(tài)碼蛔钙。
4. 304-Not Modified(未滿(mǎn)足條件的URI), 該狀態(tài)碼表示客戶(hù)端發(fā)送附帶條件的請(qǐng)求時(shí), 服務(wù)器允許請(qǐng)求訪(fǎng)問(wèn)資源, 如果未滿(mǎn)足條件, 則返回304。
5. 307-Temporary Redirect(臨時(shí)重定向), 該狀態(tài)碼與302有相同的意義, 302禁止POST變換成GET, 但是在實(shí)際使用中, 大家并不遵循, 仍然將POST轉(zhuǎn)換成了GET荠医。307會(huì)遵照標(biāo)準(zhǔn), 不會(huì)從POST變成GET吁脱。
注: 當(dāng)301、302彬向、303狀態(tài)碼返回時(shí), 幾乎所有的瀏覽器都會(huì)把POST改成GET, 并刪除請(qǐng)求報(bào)文內(nèi)的主體, 之后請(qǐng)求會(huì)自動(dòng)再次發(fā)送兼贡。即使301, 302禁止將POST方法改成GET方法, 但實(shí)際使用中大家仍然將其改成了GET。
到這里, 原因已經(jīng)很明了了娃胆。
問(wèn)題解決
對(duì)于這里的問(wèn)題場(chǎng)景, 我們不希望POST請(qǐng)求被改成GET請(qǐng)求, 則解決方法有:
如果可以, 將客戶(hù)端發(fā)起的HTTP請(qǐng)求改為HTTPS請(qǐng)求, 這樣便不會(huì)重定向紧显。
將Nginx配置文件中的
return 301 $URI
永久重定向改為return 307 $URI
臨時(shí)重定向。