由form表單來說說前后臺(tái)數(shù)據(jù)之間的交互

前言

今天想要講的東西比較雜亂,自己理了好久的思路感覺一直找不到一條線串聯(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表單有兩種提交方法:

  1. 上面的使用button來收集表單信息然后校驗(yàn)并提交到服務(wù)器
  2. 直接在form表單中添加type=submit屬性的input或者button類型的按鈕,點(diǎn)擊按鈕后表單根據(jù)actionmethod的配置直接提交表單。

對(duì)于后一種提交表單的方式還有很多配置選項(xiàng),包括enctype、acceptaccept-charset。

當(dāng)然上面可以配置的屬性在我們即將要講的Jquery的ajax方法中都是可以配置的。所以我們先說說上面代碼中涉及到的幾個(gè)知識(shí)點(diǎn)。

1.1、type="submit" vs type="button"

  1. type="submit"是將表單提交(即form.submit()方法)作為其onclick后的默認(rèn)事件。

  2. type="submit"會(huì)自動(dòng)將所有具有name屬性的html輸入元素(包括input標(biāo)簽、button標(biāo)簽、select標(biāo)簽等)都將作為鍵值對(duì)提交。

  3. 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ù)配置的值都是些什么東東呢?常見的編碼類型有:

  1. application/x-www-form-urlencoded:窗體數(shù)據(jù)被編碼為名稱/值對(duì)。這是標(biāo)準(zhǔn)的編碼格式。 (表單默認(rèn)的提交數(shù)據(jù)的格式)

  2. multipart/form-data:窗體數(shù)據(jù)被編碼為一條消息,頁上的每個(gè)控件對(duì)應(yīng)消息中的一個(gè)部分。 (上傳文件之時(shí)使用)

  3. text/plain:窗體數(shù)據(jù)以純文本形式進(jìn)行編碼,其中不含任何控件或格式字符。

  4. 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"簡(jiǎn)寫,首先會(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):

  1. 如果使用的方法不是POST或者GET,或者使用了POST方法但是Content-Type的配置不是application/x-www-form-urlencoded, multipart/form-data, or text/plain中的任意一個(gè)
  2. 如果在請(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測(cè)到你跨域了并且是使用的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ù)出場(chǎng)了:

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ù)器這邊的處理比較簡(jiǎn)單,可以直接判斷請(qǐng)求的類型,如果不是想要地編碼類型可以直接回應(yīng)415(Unsupported Media Type),如果符合要求那么服務(wù)器將根據(jù)前后端約定的來獲取請(qǐng)求的參數(shù),以express為例子,獲取參數(shù)有三種方法:官網(wǎng)介紹如下:

  1. Checks route params (req.params), ex: /user/:id
  2. Checks query string params (req.query), ex: ?id=12
  3. Checks urlencoded body params (req.body), ex: id=

分別舉個(gè)例子:

  1. 例如:127.0.0.1:3000/index,這種情況下,我們?yōu)榱说玫絠ndex,我們可以通過使用req.params得到,通過這種方法我們就可以很好的處理Node中的路由處理問題,同時(shí)利用這點(diǎn)可以非常方便的實(shí)現(xiàn)MVC模式;
  2. 例如:127.0.0.1:3000/index?id=12,這種情況下,這種方式是獲取客戶端get方式傳遞過來的值,通過使用req.query.id就可以獲得,類似于PHP的get方法;
  3. 例如:127.0.0.1:300/index,然后post了一個(gè)id=2的值,這種方式是獲取客戶端post過來的數(shù)據(jù),可以通過req.body.id獲取,類似于PHP的post方法;

4、后記

啊哈~~~最后寫下來貌似還是有點(diǎn)邏輯的哈??磥砦恼掠械臅r(shí)候還是需要在寫的時(shí)候慢慢理清邏輯,貌似我get到了什么新技能。。。。

參考文章

  1. http://api.jquery.com/jQuery.ajax/
  2. http://www.css88.com/jqapi-1.9/jQuery.ajax/
  3. http://tool.chinaz.com/pagestatus/

最后

歡迎訪問我的個(gè)人博客主頁:豆米的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容