前言
今天想要講的東西比較雜亂为严,自己理了好久的思路感覺一直找不到一條線串聯(lián)起這些碎片化的知識(shí)豁护。然后就想著那就先寫寫看吧,寫到哪算哪,最后再調(diào)整調(diào)整归园。所以童鞋們看的時(shí)候就不要太在意邏輯哈吞杭。1廊遍、從form表單提交說起
為什么從表單提交說起呢踪旷?因?yàn)榇蟛糠峙c后臺(tái)的交互都是在form表單中實(shí)現(xiàn),恰巧我入職一個(gè)月來都是在處理與后臺(tái)交互的數(shù)據(jù)整合中度過衙解,期間也發(fā)現(xiàn)一些小坑阳柔,出于喜歡總結(jié),所以才想寫這篇小博客蚓峦。
各位童鞋舌剂,可以先看一下這個(gè)例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>testing form group</title>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script type="text/javascript">
function onSubmit(){
var finalRes = $('#form').serializeArray().reduce(function(result, item){
result[item.name] = item.value;
return result;
}, {})
}
</script>
</head>
<body>
<form action="" method="post" name='info' id='form'>
<input type="text" name="user" />
<input type="text" name="email" />
</form>
<button type="button" name='submit' onclick="onSubmit()">提交</button>
</body>
</html>
Tips:
對(duì)于form表單有兩種提交方法:
- 上面的使用button來收集表單信息然后校驗(yàn)并提交到服務(wù)器
- 直接在form表單中添加
type=submit
屬性的input或者button類型的按鈕,點(diǎn)擊按鈕后表單根據(jù)action
和method
的配置直接提交表單暑椰。
對(duì)于后一種提交表單的方式還有很多配置選項(xiàng)霍转,包括enctype
、accept
和accept-charset
干茉。
當(dāng)然上面可以配置的屬性在我們即將要講的Jquery的ajax方法中都是可以配置的。所以我們先說說上面代碼中涉及到的幾個(gè)知識(shí)點(diǎn)很泊。
1.1角虫、type="submit" vs type="button"
type="submit"
是將表單提交(即form.submit()
方法)作為其onclick后的默認(rèn)事件沾谓。type="submit"
會(huì)自動(dòng)將所有具有name屬性的html輸入元素(包括input標(biāo)簽、button標(biāo)簽戳鹅、select標(biāo)簽等)都將作為鍵值對(duì)提交均驶。type="submit"
submit會(huì)有一個(gè)跳轉(zhuǎn),頁面會(huì)刷新枫虏;
上面的3點(diǎn)對(duì)于type="button"
統(tǒng)統(tǒng)都沒有妇穴,type="button"
只是一個(gè)單純的點(diǎn)擊,點(diǎn)擊事件需要你自己添加隶债,就如上面的代碼那樣腾它。
如果一切都是你說的那樣:type="submit"
會(huì)自動(dòng)提交所有的表單信息,那么我們?nèi)绾涡r?yàn)參數(shù)呢死讹?這時(shí)候便是使用表單事件處理函數(shù)onSubmit
,你可以在函數(shù)內(nèi)部校驗(yàn)參數(shù)瞒滴,不成功就返回false,這樣就可以阻止表單的提交了赞警。
另外該事件處理函數(shù)不是肯定會(huì)被調(diào)用的妓忍,條件如下:
The submit event is raised when the user clicks a submit button in a form (<input type="submit"/>).
The submit event is not raised when the user calls form.submit() function directly.
1.2、.serializeArray()
這個(gè)是Jquery的方法愧旦,目的是為了搜集表單元素內(nèi)部所有可以搜集的標(biāo)簽的name和value世剖,然后組合成類似這種形式的對(duì)象值:{name:"xxx",value:"xxx"}。于是如果表單中有多個(gè)輸入選項(xiàng)那么結(jié)果應(yīng)該是這樣的:
[{name:"xxx",value:"xxx"},{name:"xxx",value:"xxx"}....]
1.3笤虫、reduce()
這個(gè)是ES6新加的語法旁瘫,具體用法可以參考MDN。
然后我們?cè)谶@里使用它是為了將剛才序列化之后的值轉(zhuǎn)變?yōu)镴son數(shù)據(jù)耕皮,以上面的代碼為例子其結(jié)果應(yīng)該是這樣的:
{user:"xxxx",email:"xxxxxx"}
這個(gè)時(shí)候這個(gè)結(jié)果便是我們想要提交給后臺(tái)服務(wù)器的數(shù)據(jù)境蜕,那么問題來了,我們是可以直接將這個(gè)數(shù)據(jù)提交給服務(wù)器嗎凌停?中間還需要做些什么嗎粱年?2、使用$.ajax()
用過Jquery的童鞋肯定都用過$.ajax()
這個(gè)函數(shù)罚拟,其最普通的用法便是:
$.ajax({
type: 'POST',
url: ,
data: ,
dataType: 'json',
success: function(data){successCallback(data)},
error: function(jqXHR){failureCallback(jqXHR)},
})
那么問題來了:你該如何配置正確的參數(shù)才能讓你的前后臺(tái)能夠完美的協(xié)作呢台诗?
2.1、ajax方法中幾個(gè)重要的參數(shù)
2.1.1. contentType
默認(rèn)值為(application/x-www-form-urlencoded; charset=UTF-8)赐俗。根據(jù)Jquery的API文檔我們知道:
當(dāng)將數(shù)據(jù)發(fā)送到服務(wù)器時(shí)拉队,使用該內(nèi)容類型(或者叫編碼類型)。默認(rèn)值是"application/x-www-form-urlencoded; charset=UTF-8"阻逮,適合大多數(shù)情況粱快。如果你明確地傳遞了一個(gè)內(nèi)容類型(Content-Type)給 $.ajax(),那么他總是會(huì)發(fā)送給服務(wù)器(即使沒有數(shù)據(jù)要發(fā)送)。從 jQuery 1.6 開始事哭,你可以傳遞false來告訴jQuery漫雷,沒有設(shè)置任何內(nèi)容類型頭信息。 注意:W3C的XMLHttpRequest的規(guī)范規(guī)定鳍咱,數(shù)據(jù)將總是使用UTF-8字符集傳遞給服務(wù)器降盹;指定其他字符集無法強(qiáng)制瀏覽器更改編碼。 注意:對(duì)于跨域請(qǐng)求谤辜,內(nèi)容類型設(shè)置為application/x-www-form-urlencoded, multipart/form-data, 或 text/plain以外蓄坏, 將觸發(fā)瀏覽器發(fā)送一個(gè)預(yù)檢OPTIONS請(qǐng)求到服務(wù)器。
那么我們就想知道這個(gè)參數(shù)配置的值都是些什么東東呢丑念?常見的編碼類型有:
application/x-www-form-urlencoded:窗體數(shù)據(jù)被編碼為名稱/值對(duì)涡戳。這是標(biāo)準(zhǔn)的編碼格式。 (表單默認(rèn)的提交數(shù)據(jù)的格式)
multipart/form-data:窗體數(shù)據(jù)被編碼為一條消息渠欺,頁上的每個(gè)控件對(duì)應(yīng)消息中的一個(gè)部分妹蔽。 (上傳文件之時(shí)使用)
text/plain:窗體數(shù)據(jù)以純文本形式進(jìn)行編碼,其中不含任何控件或格式字符挠将。
application/json:窗體數(shù)據(jù)以Json的數(shù)據(jù)格式來傳遞胳岂。(傳遞[{},{},{}]這種json數(shù)組格式)
2.1.2. data
發(fā)送到服務(wù)器的數(shù)據(jù)。它被轉(zhuǎn)換成一個(gè)查詢字符串,如果已經(jīng)是一個(gè)字符串的話就不會(huì)轉(zhuǎn)換舔稀。查詢字符串將被追加到GET請(qǐng)求的URL后面乳丰。參見 processData 選項(xiàng)說明,以防止這種自動(dòng)轉(zhuǎn)換内贮。對(duì)象必須為"{鍵:值}"格式产园。如果這個(gè)參數(shù)是一個(gè)數(shù)組,jQuery會(huì)按照traditional 參數(shù)的值夜郁, 將自動(dòng)轉(zhuǎn)化為一個(gè)同名的多值查詢字符串什燕。
補(bǔ)充一點(diǎn)就是即使是在對(duì)象里的key/value中value是數(shù)組,也會(huì)自動(dòng)轉(zhuǎn)換成一個(gè)同名多址的字符串竞端。比如將上面例子的代碼修改成:
<script type="text/javascript">
function onSubmit(){
/*var finalRes = $('#form').serializeArray().reduce(function(result, item){
result[item.name] = item.value;
return result;
}, {})
console.log(finalRes)
*/
$.ajax({
type: 'POST',
url: 'http://blog.5udou.cn/test/first',
data: {name: 'linxiaowu', pass: '123456', weekDays: [1,2,3,4]},
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
}
</script>
那么瀏覽器將解析成這樣的效果:
2.1.3. dataType
從服務(wù)器返回你期望的數(shù)據(jù)類型屎即。 如果沒有指定,jQuery將嘗試通過MIME類型的響應(yīng)信息來智能判斷(一個(gè)XML MIME類型就被識(shí)別為XML事富,在1.4中 JSON將生成一個(gè)JavaScript對(duì)象技俐,在1.4中 script 將執(zhí)行該腳本,其他任何類型會(huì)返回一個(gè)字符串)统台。 可用的類型(以及結(jié)果作為第一個(gè)參數(shù)傳遞給成功回調(diào)函數(shù))有:
"xml": 返回 XML 文檔雕擂,可以通過 jQuery 處理。
"html": 返回純文本 HTML 文本贱勃;包含的script標(biāo)簽會(huì)在插入DOM時(shí)執(zhí)行井赌。
"script": 把響應(yīng)的結(jié)果當(dāng)作 JavaScript 執(zhí)行谤逼,并將其當(dāng)作純文本返回。默認(rèn)情況下會(huì)通過在URL中附加查詢字符串變量 仇穗,_=[TIMESTAMP]森缠, 禁用緩存結(jié)果,除非設(shè)置了cache參數(shù)為true仪缸。注意: 在遠(yuǎn)程請(qǐng)求時(shí)(不在同一個(gè)域下),所有POST請(qǐng)求都將轉(zhuǎn)為GET請(qǐng)求列肢。(愚人碼頭注:因?yàn)閷⑹褂肈OM的script標(biāo)簽來加載)
"json":把響應(yīng)的結(jié)果當(dāng)作 JSON 執(zhí)行恰画,并返回一個(gè)JavaScript對(duì)象〈陕恚跨域"json" 請(qǐng)求轉(zhuǎn)換為"jsonp"拴还,除非該請(qǐng)求在其請(qǐng)求選項(xiàng)中設(shè)置了jsonp:false。JSON 數(shù)據(jù)以嚴(yán)格的方式解析; 任何畸形的JSON將被拒絕欧聘,并且拋出解析錯(cuò)誤信息片林。在jQuery1.9中,一個(gè)空響應(yīng)也將被拒絕;服務(wù)器應(yīng)該返回null或 {}響應(yīng)代替怀骤。(見json.org的更多信息费封,正確的JSON格式。)
"jsonp": 以 JSONP 的方式載入 JSON 數(shù)據(jù)塊蒋伦。會(huì)自動(dòng)在所請(qǐng)求的URL最后添加"?callback=?"弓摘。默認(rèn)情況下會(huì)通過在URL中附加查詢字符串變量 ,_=[TIMESTAMP]痕届, 禁用緩存結(jié)果韧献,除非設(shè)置了cache參數(shù)為true。
"text": 返回純文本字符串研叫。
多個(gè)用空格分割的值:從 jQuery 1.5 開始锤窑, jQuery可以內(nèi)容類型(Content-Type)頭收到并轉(zhuǎn)換一個(gè)您需要的數(shù)據(jù)類型。例如嚷炉,如果你想要一個(gè)文本響應(yīng)為XML處理渊啰,使用"text xml"數(shù)據(jù)類型。您也可以將一個(gè)JSONP的請(qǐng)求渤昌,以文本形式接受虽抄,并用jQuery以XML解析: "jsonp text xml"。同樣地可以使用"jsonp xml"簡寫,首先會(huì)嘗試從 jsonp 到 xml 的轉(zhuǎn)換独柑,如果轉(zhuǎn)換失敗迈窟,就先將 jsonp 轉(zhuǎn)換成 text, 然后再由 text 轉(zhuǎn)換成 xml。
2.2忌栅、不同的method組合不同的ContentType
我們?cè)谏厦娴拇a中添加這樣一段代碼:
$.ajax({
type: 'POST',
url: 'http://blog.5udou.cn/test/first',
data: finalRes,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
幾個(gè)關(guān)鍵的參數(shù)都已經(jīng)配置车酣,那么在method='POST'
的時(shí)候曲稼,data部分是如何組裝進(jìn)請(qǐng)求中的呢?答案是:瀏覽器把form數(shù)據(jù)封裝到http body中湖员,然后發(fā)送到server贫悄,如圖:
如果是methos='GET'
的時(shí)候,則瀏覽器用x-www-form-urlencoded的編碼方式把form數(shù)據(jù)轉(zhuǎn)換成一個(gè)字串(name1=value1&name2=value2...)娘摔,然后把這個(gè)字串a(chǎn)ppend到url后面窄坦,用?分割,加載這個(gè)新的url凳寺。如圖:
如果沒有type=file的控件鸭津,用默認(rèn)的application/x-www-form-urlencoded就可以了。 但是如果有type=file的話肠缨,就要用到multipart/form-data了逆趋。瀏覽器會(huì)把整個(gè)表單以控件為單位分割,并為每個(gè)部分加上Content-Disposition(form-data或者file),Content-Type(默認(rèn)為text/plain),name(控件name)等信息晒奕,并加上分割符(boundary)闻书。
2.3、特殊的application/json
如果你想要傳遞一個(gè)json Array的話脑慧,使用application/x-www-form-urlencoded是不行的魄眉,代碼改成:
<script type="text/javascript">
function onSubmit(){
/*var finalRes = $('#form').serializeArray().reduce(function(result, item){
result[item.name] = item.value;
return result;
}, {})
console.log(finalRes)
*/
$.ajax({
type: 'POST',
url: 'http://blog.5udou.cn/test/first',
data: [{name: 'linxiaowu', pass: '123456'}, {name:'xiaomizha', pass:'123456'}],
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
}
</script>
結(jié)果如下:
于是我們需要修改成這樣:
<script type="text/javascript">
function onSubmit(){
/*var finalRes = $('#form').serializeArray().reduce(function(result, item){
result[item.name] = item.value;
return result;
}, {})
console.log(finalRes)
*/
$.ajax({
type: 'POST',
url: 'http://blog.5udou.cn/test/first',
data: [{name: 'linxiaowu', pass: '123456'}, {name:'xiaomizha', pass:'123456'}],
dataType: 'json',
contentType: 'application/json',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
}
</script>
結(jié)果如下:
咦,竟然報(bào)錯(cuò)闷袒!并且data的數(shù)據(jù)并沒有加到request里面去杆融,這是為什么?
其實(shí)控制臺(tái)的錯(cuò)誤在上面的例子都是有報(bào)錯(cuò)的霜运,關(guān)鍵在于報(bào)錯(cuò)的順序脾歇,為什么這么說呢?根據(jù)MDN的說明淘捡,我們發(fā)現(xiàn):
- 如果使用的方法不是POST或者GET藕各,或者使用了POST方法但是Content-Type的配置不是application/x-www-form-urlencoded, multipart/form-data, or text/plain中的任意一個(gè)
- 如果在請(qǐng)求中設(shè)置了自定義的頭部(比如請(qǐng)求使用了類似X-PINGOTHER的頭部)
上面兩種情況都會(huì)執(zhí)行preflighted request,也就是執(zhí)行發(fā)送請(qǐng)求前的檢查:"preflighted"請(qǐng)求首先會(huì)發(fā)送一個(gè)HTTP OPTIONS的請(qǐng)求頭部到另外一個(gè)域下焦除,這是為了決定實(shí)際的請(qǐng)求是不是可以安全地發(fā)送激况。
所以你在上圖中看到紅色框圈起來的METHOD為OPTIONS就是因?yàn)闄z測到你跨域了并且是使用的application/json
+ POST
的請(qǐng)求方法,所以才會(huì)出現(xiàn)剛才的情況膘魄,找不到發(fā)送的數(shù)據(jù)乌逐,請(qǐng)求方法也是不對(duì)的。
那么針對(duì)這種情況创葡,我們修改請(qǐng)求的URL為同源的即可浙踢,這里在本地假設(shè)一個(gè)Express服務(wù)器,于是有:
咦灿渴?還是有錯(cuò)誤洛波。這個(gè)時(shí)候就需要JSON.stringify
函數(shù)出場了:
function onSubmit(){
var data = JSON.stringify([{name: "linxiaowu", pass: "123456"}, {name:"xiaomizha", pass:"123456"}])
$.ajax({
type: 'POST',
url: '/test/first',
data: data,
dataType: 'json',
contentType: 'application/json',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
}
這個(gè)時(shí)候結(jié)果就是我們想要的:
2.3.1胰舆、Tips
JSON.stringify
turns a Javascript object into JSON text and stores that JSON text in a string.
JSON.parse
turns a string of JSON text into a Javascript object.
2.3.2、問題
留給童鞋們一個(gè)問題:在上面的例子代碼中直接將data的值賦給data蹬挤,即:
$.ajax({
type: 'POST',
url: '/test/first',
data: [{"name":"linxiaowu","pass":"123456"},{"name":"xiaomizha","pass":"123456"}],
dataType: 'json',
contentType: 'application/json',
success: function(data){console.log(data)},
error: function(jqXHR){console.log(jqXHR)},
})
結(jié)果是錯(cuò)誤的缚窿,識(shí)別不到這是一個(gè)JSON,這是為什么呢焰扳?倦零?
3、服務(wù)器的反應(yīng)呢吨悍?
服務(wù)器這邊的處理比較簡單光绕,可以直接判斷請(qǐng)求的類型,如果不是想要地編碼類型可以直接回應(yīng)415(Unsupported Media Type)畜份,如果符合要求那么服務(wù)器將根據(jù)前后端約定的來獲取請(qǐng)求的參數(shù),以express為例子欣尼,獲取參數(shù)有三種方法:官網(wǎng)介紹如下:
- Checks route params (req.params), ex: /user/:id
- Checks query string params (req.query), ex: ?id=12
- Checks urlencoded body params (req.body), ex: id=
分別舉個(gè)例子:
- 例如:127.0.0.1:3000/index爆雹,這種情況下,我們?yōu)榱说玫絠ndex愕鼓,我們可以通過使用req.params得到钙态,通過這種方法我們就可以很好的處理Node中的路由處理問題,同時(shí)利用這點(diǎn)可以非常方便的實(shí)現(xiàn)MVC模式菇晃;
- 例如:127.0.0.1:3000/index?id=12册倒,這種情況下,這種方式是獲取客戶端get方式傳遞過來的值磺送,通過使用req.query.id就可以獲得驻子,類似于PHP的get方法;
- 例如:127.0.0.1:300/index估灿,然后post了一個(gè)id=2的值崇呵,這種方式是獲取客戶端post過來的數(shù)據(jù),可以通過req.body.id獲取馅袁,類似于PHP的post方法域慷;
4、后記
啊哈~~~最后寫下來貌似還是有點(diǎn)邏輯的哈汗销∮贪看來文章有的時(shí)候還是需要在寫的時(shí)候慢慢理清邏輯,貌似我get到了什么新技能弛针。叠骑。。削茁。參考文章
- http://api.jquery.com/jQuery.ajax/
- http://www.css88.com/jqapi-1.9/jQuery.ajax/
- http://tool.chinaz.com/pagestatus/
最后
歡迎訪問我的個(gè)人博客主頁:豆米的博客