瀏覽器有別_HTTP報文的回車換行
本來以為瀏覽器HTTP報文的生成應該是完全一致的。但最近在做一個項目的時候,發現Safari和Chrome提交同一份表單,後端的處理結果不一致。看提交結果呢,是因為Safari多了個回車。由於原項目的提交數據比較複雜,我就寫了簡單的測試來加以驗證。
說是測試,其實也是驗證心裡的想法:正常的HTTP報文每行結尾符一般用\r\n
,那如果我提交的文本裡面帶了\r
或\n
,那瀏覽器會不會主動補充成\r\n
呢?
從現在的情況來看,Safari是會主動補充成\r\n
,而Google不會。接下來就來測試一下。
測試前的準備
一、準備一個測試用的Web服務
const Koa = require('koa');
const koaBody = require('koa-body');
const Router = require("koa-router");
const router = new Router();
const app = new Koa();
app.use(koaBody({ multipart: true }));
router.get("/browser/returntest", (ctx) => {
ctx.status = 200;
ctx.body = "<form method='post' action='/browser/returntest'><textarea name='str'></textarea><button type='submit'></button></form>";
});
router.post("/browser/returntest", (ctx) => {
console.log(ctx.request.body)
ctx.status = 200;
ctx.body = {
isSuccessful: true,
data: "post,成功",
errCode: 0
};
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3030);
二、選定測試參數
這次測試主要選定了幾個可以上傳鍵值對的Content Type,分別是application/json、application/x-www-form-urlencoded、multipart/form-data。
按理來說:json數據字元串經過轉義後\n
就是\\n
,本來就不是換行,而是反斜杠和字母n;urlencoded會進行url編碼,\n
就是%0a
,也不是換行;而form-data的換行就是換行。
三、選擇測試工具
我用的是Charles,用起來比起直接在瀏覽器看報文更加自由一點。
我平時主要是爬蟲需要進行模擬登錄時或者爬取手機端時,用它來分析請求報文。
開始測試
一、編寫測試腳本
-
application/json格式的請求
var xhr = new XMLHttpRequest; xhr.open("post", "/browser/returntest"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.send(JSON.stringify({ str: "abc\nabc" })); // # 等效於 // curl -H "Content-Type: application/json" -X POST --data '{"str":"abc\nabc"}' //localhost:3030/browser/returntest
-
application/x-www-form-urlencoded格式的請求
var xhr = new XMLHttpRequest; xhr.open("post", "/browser/returntest"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send("str=abc\nabc"); // # 等效於 // curl -H "Content-Type: application/x-www-form-urlencoded" -X POST --data 'str=abc // abc' //localhost:3030/browser/returntest
-
multipart/form-data格式的請求
var xhr = new XMLHttpRequest; var formData = new FormData formData.append("str", "abc\nabc"); xhr.open("post", "/browser/returntest"); xhr.send(formData); // # 等效於 // curl -H "Content-Type: multipart/form-data" -X POST --form 'str=abc // abc' //localhost:3030/browser/returntest
二、測試
- 啟動Nodejs Web服務;
- 打開瀏覽器,輸入
//localhost.charlesproxy.com:3030/browser/returntest
; - 打開Charles;
- 打開瀏覽器控制台,輸入腳本進行測試。
三、測試結果
-
提交application/json請求(Google、Safari結果一致)
把application/json格式的請求輸入到控制台,可以在Charles截獲到請求:
此時nodejs的輸出為:
Charles可以看到請求報文的數據是:
Charles可以看到請求體的十六進位表示是:
可以看到,
\n
就是\
和n
,\
對應5c
,n
對應6e
。(想確認ASCII碼的可以看對照表)application/json測試驗證猜想,json數據字元串經過轉義後
\n
就是\\n
,本來就不是換行,而是反斜杠和字母n。 -
提交application/x-www-form-urlencoded請求(Google、Safari結果一致)
把application/x-www-form-urlencoded格式的請求輸入到控制台,可以在Charles截獲到請求。
此時nodejs的輸出為:
Charles可以看到請求報文的數據是:
Charles可以看到請求體的十六進位表示是:
這和猜想的倒是有些不一樣,xhr.send的時候沒有把換行按urlencoded的格式轉成%0A,而是以換行的形式傳輸。更讓我意想不到的是,這次Safari也沒有在換行前加上回車了,我本來以為Safari補回車或換行是為了嚴謹性,結果太讓人失望了。
-
提交application/x-www-form-urlencoded請求的補充測試(Google、Safari結果一致)
xhr.send不行,也許是application/x-www-form-urlencoded格式的數據大多數時候是以表單形式直接提交的吧,那我通過表單直接提交試一下。
此時在Charles截獲到請求:
請求體的十六進位表示是:
這次編碼了,但是回車(%0D)也給加上了是什麼鬼,Google也是一樣的,這難道是瀏覽器統一的標準.
我直接在本地複製一段只有換行的文本呢?好吧,也是一樣的。
綜上,通過瀏覽器表單輸入的多行文本多會被補全成
\r\n
。nodejs的輸出都為:
-
提交multipart/form-data請求(Google、Safari結果不一致)
-
Google
把multipart/form-data格式的請求輸入到控制台,可以在Charles截獲到請求。
此時nodejs的輸出為:
Charles可以看到請求報文的數據是:
Charles可以看到請求體的十六進位表示是:
Google測試驗證猜想,abc和abc之間只有換行(0a)。
-
Safari
把multipart/form-data格式的請求輸入到控制台,可以在Charles截獲到請求。
此時nodejs的輸出為:
Charles可以看到請求報文的數據是:
Charles可以看到請求體的十六進位表示是:
Safari測試驗證猜想,abc和abc之間補充上回車(0d)。
除此之外,我還測試了
\r
上傳會不會補充\n
,結果也是會的。
-
測試小結
實驗證明,如果以multipart/form-data數據格式上傳帶回車或換行的數據,在Safari里會補充成回車換行,而Chrome不會。如果不要回車換行其中的一個,後端還要做校驗才是。
這次實驗我本來以為Safari補全成回車換行是更嚴謹的做法,但它這做事做一半就讓人很不舒服,要補把application/x-www-form-urlencoded格式的數據也補了嘛。不過從合理性上考慮,畢竟叫urlencoded,我們做數據請求的時候還是要自己做一個編碼。
var xhr = new XMLHttpRequest;
xhr.open("post", "/browser/returntest");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(encodeURI("str=abc\nabc"));
// # 等效於
// curl -H "Content-Type: application/x-www-form-urlencoded" -X POST --data 'str=abc%0aabc' //localhost:3030/browser/returntest
然後這次測試還不完善,像以multipart/form-data數據格式上傳帶回車或換行的文件是否會有問題沒有測,響應體是否也會被補充成回車換行沒有測。有興趣大家自己測一測吧。