使用瀏覽器猜煮,查看頁(yè)面撼班,點(diǎn)擊按鈕提交信息歧匈,實(shí)際上是一次次http請(qǐng)求。瀏覽器在發(fā)送這些請(qǐng)求時(shí)砰嘁,是需要對(duì)請(qǐng)求參數(shù)中的特殊字符做編碼的件炉,服務(wù)端需要對(duì)參數(shù)解碼,才能知道參數(shù)的原始內(nèi)容矮湘,再進(jìn)行處理斟冕。不同的場(chǎng)景,編碼方式是存在差別的缅阳。
GET請(qǐng)求
get請(qǐng)求的參數(shù)會(huì)在URL的中磕蛇,在“?”的后面十办。一般來(lái)說(shuō)秀撇,URL只能使用英文字母、阿拉伯?dāng)?shù)字和某些標(biāo)點(diǎn)符號(hào)向族,不能使用其他文字和符號(hào)捌袜。比如,世界上有英文字母的網(wǎng)址 http://www.abc.com炸枣,但是沒(méi)有中文網(wǎng)址 http://www.我是網(wǎng)站.com虏等。
這是因?yàn)榫W(wǎng)絡(luò)標(biāo)準(zhǔn)RFC 1738做了硬性規(guī)定:
"只有字母和數(shù)字[0-9a-zA-Z]、一些特殊符號(hào)"-_.!*'()+"[不包括雙引號(hào)]适肠,和保留字(&霍衫,?之類的)才可以不經(jīng)過(guò)編碼直接用于URL侯养。"
這個(gè)編碼是由瀏覽器完成的敦跌,編碼方式叫做“URL編碼”
場(chǎng)景:點(diǎn)擊鏈接
注意參數(shù)name含有空格
<a href="/?name=前 ++端">前端</a>
抓包得到:
GET /?name=%E5%89%8D%20%20++%E7%AB%AF HTTP/1.1
瀏覽器html文件中head標(biāo)簽中的頁(yè)面編碼方式 utf-8
<meta content="text/html; charset=utf-8" http-equiv="content-type">
把中文的“前端” 用 utf-8字符集的十六進(jìn)制 “E5898D”(前), “E7ABAF”(端)逛揩,然后用”%“分割柠傍,空格轉(zhuǎn)化成了“%20”
場(chǎng)景:form表單提交,method默認(rèn)為“get”辩稽,enctype編碼方式默認(rèn)為“application/x-www-form-urlencoded”
“application/x-www-form-urlencoded”惧笛,特殊字符編碼方式和“點(diǎn)擊鏈接”場(chǎng)景是一樣的,唯一的區(qū)別是空格會(huì)轉(zhuǎn)化為“+”逞泄,內(nèi)容中的
“+”url編碼為“%2B”
例如:
value值為“前 ++端”含有空格和+
<h2>get</h2>
<form action="/">
<label>name</label><input name="name" value="前 ++端"/>
<input type="submit" id="submit" value="提交"/>
</form>
點(diǎn)擊提交表單患整,抓包得到:
GET /?name=%E5%89%8D++%2B%2B%E7%AB%AF HTTP/1.1
ajax異步提交
let xhr = new XMLHttpRequest()
xhr.open('get', '/?name=前 ++端', true)
xhr.send()
抓包得到:
GET /?name=%E5%89%8D%20%20++%E7%AB%AF HTTP/1.1
編碼的結(jié)果和點(diǎn)擊鏈接場(chǎng)景是一致的拜效。
結(jié)論:除了form表單的get方式提交的編碼方式不一樣,其他的都一樣各谚。
POST請(qǐng)求
form表單提交
非文件表單項(xiàng)提交
默認(rèn)的enctype="application/x-www-form-urlencoded"
<h2>post</h2>
<form action="/post" method="post">
<label>name</label><input name="name" value="前 ++端"/>
<input type="submit" id="submit" value="提交"/>
</form>
點(diǎn)擊提交后紧憾,抓包得到:
POST /post HTTP/1.1
Host: localhost:3001
Content-Length: 31
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:3001
Upgrade-Insecure-Requests: 1
**Content-Type: application/x-www-form-urlencoded**
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate, br
**name=%E5%89%8D++%2B%2B%E7%AB%AF**
post請(qǐng)求傳輸?shù)膮?shù)在請(qǐng)求體中,編碼成了 “name=%E5%89%8D++%2B%2B%E7%AB%AF”昌渤,說(shuō)明使用enctype="application/x-www-form-urlencoded"方式
編碼赴穗,get和post的結(jié)果是一樣的。
另外post提交需要關(guān)注“Content-Type”這個(gè)請(qǐng)求頭膀息,這個(gè)請(qǐng)求頭告訴服務(wù)端望抽,我請(qǐng)求體內(nèi)容和編碼格式。
文件上傳
文件上傳必須使用enctype="multipart/form-data"履婉,表示請(qǐng)求體內(nèi)容不做URL編碼,原文傳輸
<h2>file</h2>
<form action="/post" id="fileupload" method="post" enctype="multipart/form-data">
<label>name</label><input name="name" value="前 ++端"/>
<label>file</label><input type="file" name="file"/>
<input type="submit" id="submit" value="提交"/>
</form>
點(diǎn)擊提交按鈕后斟览,抓包得到:
注意:Content-Type為: multipart/form-data; boundary=----WebKitFormBoundaryIAobIVW9hww2sV7i
表示編碼是multipart/form-data毁腿,而且每一個(gè)表單項(xiàng)使用“----WebKitFormBoundaryIAobIVW9hww2sV7i”作為分割線。
然后name的值“前 ++端”苛茂,也沒(méi)有做編碼處理已烤。
ajax異步提交
原生ajax
xhr.open('post', '/post', true)
xhr.send(JSON.stringify({name:'前 ++端'}))
抓包得到:
POST /post HTTP/1.1
Host: localhost:3001
Content-Length: 21
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:3001
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
DNT: 1
{"name":"前 ++端"}
ajax原生post請(qǐng)求,瀏覽器對(duì)請(qǐng)求體不做任何編碼處理妓羊,Content-Type: text/plain;charset=UTF-8
Jquery
$.ajax({
url: '/post',
method: 'post',
data: {
name: '前 ++端',
}
})
jquery對(duì)于ajax的請(qǐng)求內(nèi)容的處理胯究,默認(rèn)的編碼方式application/x-www-form-urlencoded;
POST /post HTTP/1.1
Host: localhost:3001
Content-Length: 31
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://localhost:3001
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
DNT: 1
name=%E5%89%8D++%2B%2B%E7%AB%AF
因?yàn)閖query默認(rèn)是application/x-www-form-urlencoded,但是對(duì)于使用formdata做圖片上傳的話,他不能自動(dòng)識(shí)別出formdata躁绸,需要使用enctype="multipart/form-data"裕循,只能手動(dòng)調(diào)整參數(shù)processData,contentType來(lái)解決這個(gè)問(wèn)題:
let formdata = new FormData(document.querySelector("form"));
$.ajax({
url: "/post",
type: "post",
data: formdata,
processData: false, // 不處理數(shù)據(jù)
contentType: false // 不設(shè)置內(nèi)容類型
});
axios
axios是最近在前端圈內(nèi)大面積使用的ajax框架,功能強(qiáng)悍净刮,可以自行g(shù)oogle搜索剥哑。
axios.post('/post', {
name: '前 ++端',
})
得到:
POST /post HTTP/1.1
Host: localhost:3001
Content-Length: 21
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost:3001
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Content-Type: application/json;charset=UTF-8
DNT: 1
{"name":"前 ++端"}
axios默認(rèn)編碼方式是將請(qǐng)求體內(nèi)容json stringfy,然后設(shè)置Content-Type: application/json;charset=UTF-8
在聯(lián)調(diào)是淹父,會(huì)出現(xiàn)很多服務(wù)端不支持json格式的數(shù)據(jù)的處理株婴,只支持傳統(tǒng)的application/x-www-form-urlencoded,和傳統(tǒng)的表單提交一樣的格式
這時(shí)就需要使用QS框架了
import qs from 'qs'
axios.post('/post', qs.stringify({
name: '前 ++端'
}))
這樣能夠提交application/x-www-form-urlencoded編碼的數(shù)據(jù)暑认。
axios能夠智能判斷需要提交的數(shù)據(jù)困介,如果是formdata,會(huì)自動(dòng)切換成使用multipart/form-data編碼
let formdata = new FormData(document.querySelector("form"));
axios.post('/post', formdata) //不要做任何處理蘸际,這個(gè)很贊Wā!
服務(wù)端解碼
目前使用nodejs框架koa和koa-body中間件做post請(qǐng)求參數(shù)的解碼粮彤,其他語(yǔ)言框架的處理思維也是一致的八回。
GET請(qǐng)求
decodeURIComponent(str.replace(/\+/g, ' '));
將使用form表單get請(qǐng)求酷愧,使用‘a(chǎn)pplication/x-www-form-urlencoded’編碼后的’+’,轉(zhuǎn)化成空格缠诅,再URL解碼
so easyH茉 !管引!
POST請(qǐng)求
ctx.is 是做判斷士败,進(jìn)入不同的處理邏輯,ctx.js是什么褥伴?
var value = req.headers['content-type’]
return typeis(value, types)
ctx.js是服務(wù)端取請(qǐng)求頭中content-type谅将,做類型判斷。
所以只要 content-type 和 請(qǐng)求體內(nèi)容正確的一一對(duì)應(yīng)重慢,那么服務(wù)端就能正確的解碼出瀏覽器傳輸過(guò)來(lái)的內(nèi)容
json字符串:Content-Type: application/json;charset=UTF-8
表單提交饥臂,URL編碼:Content-Type: application/x-www-form-urlencoded; charset=UTF-8
文件上傳:Content-Type為: multipart/form-data; boundary=----WebKitFormBoundaryIAobIVW9hww2sV7i