前言
今天想要講的東西比較雜亂,自己理了好久的思路感覺一直找不到一條線串聯起這些碎片化的知識。然后就想著那就先寫寫看吧,寫到哪算哪,最后再調整調整。所以童鞋們看的時候就不要太在意邏輯哈。1、從form表單提交說起
為什么從表單提交說起呢?因為大部分與后臺的交互都是在form表單中實現,恰巧我入職一個月來都是在處理與后臺交互的數據整合中度過,期間也發現一些小坑,出于喜歡總結,所以才想寫這篇小博客。
各位童鞋,可以先看一下這個例子:
<!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:
對于form表單有兩種提交方法:
- 上面的使用button來收集表單信息然后校驗并提交到服務器
- 直接在form表單中添加
type=submit
屬性的input或者button類型的按鈕,點擊按鈕后表單根據action
和method
的配置直接提交表單。
對于后一種提交表單的方式還有很多配置選項,包括enctype
、accept
和accept-charset
。
當然上面可以配置的屬性在我們即將要講的Jquery的ajax方法中都是可以配置的。所以我們先說說上面代碼中涉及到的幾個知識點。
1.1、type="submit" vs type="button"
type="submit"
是將表單提交(即form.submit()
方法)作為其onclick后的默認事件。type="submit"
會自動將所有具有name屬性的html輸入元素(包括input標簽、button標簽、select標簽等)都將作為鍵值對提交。type="submit"
submit會有一個跳轉,頁面會刷新;
上面的3點對于type="button"
統統都沒有,type="button"
只是一個單純的點擊,點擊事件需要你自己添加,就如上面的代碼那樣。
如果一切都是你說的那樣:type="submit"
會自動提交所有的表單信息,那么我們如何校驗參數呢?這時候便是使用表單事件處理函數onSubmit
,你可以在函數內部校驗參數,不成功就返回false,這樣就可以阻止表單的提交了。
另外該事件處理函數不是肯定會被調用的,條件如下:
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()
這個是Jquery的方法,目的是為了搜集表單元素內部所有可以搜集的標簽的name和value,然后組合成類似這種形式的對象值:{name:"xxx",value:"xxx"}。于是如果表單中有多個輸入選項那么結果應該是這樣的:
[{name:"xxx",value:"xxx"},{name:"xxx",value:"xxx"}....]
1.3、reduce()
這個是ES6新加的語法,具體用法可以參考MDN。
然后我們在這里使用它是為了將剛才序列化之后的值轉變為Json數據,以上面的代碼為例子其結果應該是這樣的:
{user:"xxxx",email:"xxxxxx"}
這個時候這個結果便是我們想要提交給后臺服務器的數據,那么問題來了,我們是可以直接將這個數據提交給服務器嗎?中間還需要做些什么嗎?2、使用$.ajax()
用過Jquery的童鞋肯定都用過$.ajax()
這個函數,其最普通的用法便是:
$.ajax({
type: 'POST',
url: ,
data: ,
dataType: 'json',
success: function(data){successCallback(data)},
error: function(jqXHR){failureCallback(jqXHR)},
})
那么問題來了:你該如何配置正確的參數才能讓你的前后臺能夠完美的協作呢?
2.1、ajax方法中幾個重要的參數
2.1.1. contentType
默認值為(application/x-www-form-urlencoded; charset=UTF-8)。根據Jquery的API文檔我們知道:
當將數據發送到服務器時,使用該內容類型(或者叫編碼類型)。默認值是"application/x-www-form-urlencoded; charset=UTF-8",適合大多數情況。如果你明確地傳遞了一個內容類型(Content-Type)給 $.ajax(),那么他總是會發送給服務器(即使沒有數據要發送)。從 jQuery 1.6 開始,你可以傳遞false來告訴jQuery,沒有設置任何內容類型頭信息。 注意:W3C的XMLHttpRequest的規范規定,數據將總是使用UTF-8字符集傳遞給服務器;指定其他字符集無法強制瀏覽器更改編碼。 注意:對于跨域請求,內容類型設置為application/x-www-form-urlencoded, multipart/form-data, 或 text/plain以外, 將觸發瀏覽器發送一個預檢OPTIONS請求到服務器。
那么我們就想知道這個參數配置的值都是些什么東東呢?常見的編碼類型有:
application/x-www-form-urlencoded:窗體數據被編碼為名稱/值對。這是標準的編碼格式。 (表單默認的提交數據的格式)
multipart/form-data:窗體數據被編碼為一條消息,頁上的每個控件對應消息中的一個部分。 (上傳文件之時使用)
text/plain:窗體數據以純文本形式進行編碼,其中不含任何控件或格式字符。
application/json:窗體數據以Json的數據格式來傳遞。(傳遞[{},{},{}]這種json數組格式)
2.1.2. data
發送到服務器的數據。它被轉換成一個查詢字符串,如果已經是一個字符串的話就不會轉換。查詢字符串將被追加到GET請求的URL后面。參見 processData 選項說明,以防止這種自動轉換。對象必須為"{鍵:值}"格式。如果這個參數是一個數組,jQuery會按照traditional 參數的值, 將自動轉化為一個同名的多值查詢字符串。
補充一點就是即使是在對象里的key/value中value是數組,也會自動轉換成一個同名多址的字符串。比如將上面例子的代碼修改成:
<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
從服務器返回你期望的數據類型。 如果沒有指定,jQuery將嘗試通過MIME類型的響應信息來智能判斷(一個XML MIME類型就被識別為XML,在1.4中 JSON將生成一個JavaScript對象,在1.4中 script 將執行該腳本,其他任何類型會返回一個字符串)。 可用的類型(以及結果作為第一個參數傳遞給成功回調函數)有:
"xml": 返回 XML 文檔,可以通過 jQuery 處理。
"html": 返回純文本 HTML 文本;包含的script標簽會在插入DOM時執行。
"script": 把響應的結果當作 JavaScript 執行,并將其當作純文本返回。默認情況下會通過在URL中附加查詢字符串變量 ,_=[TIMESTAMP], 禁用緩存結果,除非設置了cache參數為true。注意: 在遠程請求時(不在同一個域下),所有POST請求都將轉為GET請求。(愚人碼頭注:因為將使用DOM的script標簽來加載)
"json":把響應的結果當作 JSON 執行,并返回一個JavaScript對象。跨域"json" 請求轉換為"jsonp",除非該請求在其請求選項中設置了jsonp:false。JSON 數據以嚴格的方式解析; 任何畸形的JSON將被拒絕,并且拋出解析錯誤信息。在jQuery1.9中,一個空響應也將被拒絕;服務器應該返回null或 {}響應代替。(見json.org的更多信息,正確的JSON格式。)
"jsonp": 以 JSONP 的方式載入 JSON 數據塊。會自動在所請求的URL最后添加"?callback=?"。默認情況下會通過在URL中附加查詢字符串變量 ,_=[TIMESTAMP], 禁用緩存結果,除非設置了cache參數為true。
"text": 返回純文本字符串。
多個用空格分割的值:從 jQuery 1.5 開始, jQuery可以內容類型(Content-Type)頭收到并轉換一個您需要的數據類型。例如,如果你想要一個文本響應為XML處理,使用"text xml"數據類型。您也可以將一個JSONP的請求,以文本形式接受,并用jQuery以XML解析: "jsonp text xml"。同樣地可以使用"jsonp xml"簡寫,首先會嘗試從 jsonp 到 xml 的轉換,如果轉換失敗,就先將 jsonp 轉換成 text, 然后再由 text 轉換成 xml。
2.2、不同的method組合不同的ContentType
我們在上面的代碼中添加這樣一段代碼:
$.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)},
})
幾個關鍵的參數都已經配置,那么在method='POST'
的時候,data部分是如何組裝進請求中的呢?答案是:瀏覽器把form數據封裝到http body中,然后發送到server,如圖:
如果是methos='GET'
的時候,則瀏覽器用x-www-form-urlencoded的編碼方式把form數據轉換成一個字串(name1=value1&name2=value2...),然后把這個字串append到url后面,用?分割,加載這個新的url。如圖:
如果沒有type=file的控件,用默認的application/x-www-form-urlencoded就可以了。 但是如果有type=file的話,就要用到multipart/form-data了。瀏覽器會把整個表單以控件為單位分割,并為每個部分加上Content-Disposition(form-data或者file),Content-Type(默認為text/plain),name(控件name)等信息,并加上分割符(boundary)。
2.3、特殊的application/json
如果你想要傳遞一個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>
結果如下:
于是我們需要修改成這樣:
<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>
結果如下:
咦,竟然報錯!并且data的數據并沒有加到request里面去,這是為什么?
其實控制臺的錯誤在上面的例子都是有報錯的,關鍵在于報錯的順序,為什么這么說呢?根據MDN的說明,我們發現:
- 如果使用的方法不是POST或者GET,或者使用了POST方法但是Content-Type的配置不是application/x-www-form-urlencoded, multipart/form-data, or text/plain中的任意一個
- 如果在請求中設置了自定義的頭部(比如請求使用了類似X-PINGOTHER的頭部)
上面兩種情況都會執行preflighted request,也就是執行發送請求前的檢查:"preflighted"請求首先會發送一個HTTP OPTIONS的請求頭部到另外一個域下,這是為了決定實際的請求是不是可以安全地發送。
所以你在上圖中看到紅色框圈起來的METHOD為OPTIONS就是因為檢測到你跨域了并且是使用的application/json
+ POST
的請求方法,所以才會出現剛才的情況,找不到發送的數據,請求方法也是不對的。
那么針對這種情況,我們修改請求的URL為同源的即可,這里在本地假設一個Express服務器,于是有:
咦?還是有錯誤。這個時候就需要JSON.stringify
函數出場了:
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)},
})
}
這個時候結果就是我們想要的:
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、問題
留給童鞋們一個問題:在上面的例子代碼中直接將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)},
})
結果是錯誤的,識別不到這是一個JSON,這是為什么呢??
3、服務器的反應呢?
服務器這邊的處理比較簡單,可以直接判斷請求的類型,如果不是想要地編碼類型可以直接回應415(Unsupported Media Type),如果符合要求那么服務器將根據前后端約定的來獲取請求的參數,以express為例子,獲取參數有三種方法:官網介紹如下:
- 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=
分別舉個例子:
- 例如:127.0.0.1:3000/index,這種情況下,我們為了得到index,我們可以通過使用req.params得到,通過這種方法我們就可以很好的處理Node中的路由處理問題,同時利用這點可以非常方便的實現MVC模式;
- 例如:127.0.0.1:3000/index?id=12,這種情況下,這種方式是獲取客戶端get方式傳遞過來的值,通過使用req.query.id就可以獲得,類似于PHP的get方法;
- 例如:127.0.0.1:300/index,然后post了一個id=2的值,這種方式是獲取客戶端post過來的數據,可以通過req.body.id獲取,類似于PHP的post方法;
4、后記
啊哈~~~最后寫下來貌似還是有點邏輯的哈。看來文章有的時候還是需要在寫的時候慢慢理清邏輯,貌似我get到了什么新技能。。。。參考文章
- http://api.jquery.com/jQuery.ajax/
- http://www.css88.com/jqapi-1.9/jQuery.ajax/
- http://tool.chinaz.com/pagestatus/
最后
歡迎訪問我的個人博客主頁:豆米的博客