小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

ThinkJS 中數(shù)據(jù)解析和校驗(yàn)

 jswcy163 2018-07-11

編者按:本文轉(zhuǎn)載自 ThinkJS

Web 服務(wù)的整個(gè)流程中,獲取數(shù)據(jù)是最重要的一環(huán),如何方便快捷的獲取用戶提交的數(shù)據(jù)減少開發(fā)者的開發(fā)成本是一個(gè) Web 服務(wù)框架應(yīng)該考慮的事情。本文將會(huì)為大家介紹在 ThinkJS 中請(qǐng)求數(shù)據(jù)解析和數(shù)據(jù)校驗(yàn)相關(guān)的內(nèi)容。

暫且不提 HTTP/2,HTTP 請(qǐng)求本質(zhì)上是一個(gè)有一定格式的文本字符串,程序按照規(guī)范格式解析后就能獲得我們想要的數(shù)據(jù)。不同的編碼方式需要我們使用不同的規(guī)范來解析請(qǐng)求體,在 ThinkJS 中提供了強(qiáng)大的數(shù)據(jù)解析中間件 think-payload,非常方便的將多種類型的請(qǐng)求體數(shù)據(jù)自動(dòng)轉(zhuǎn)換為 JavaScript 對(duì)象。

當(dāng)然在實(shí)現(xiàn)業(yè)務(wù)的過程中,我們發(fā)現(xiàn)簡(jiǎn)單的解析請(qǐng)求數(shù)據(jù)傳給業(yè)務(wù)層是不夠的,我們往往需要對(duì)用戶數(shù)據(jù)進(jìn)行各種校驗(yàn)和過濾,例如數(shù)據(jù)類型校驗(yàn)以及數(shù)據(jù)合法性校驗(yàn)等等。如果將這部分代碼和業(yè)務(wù)耦合在一塊的話會(huì)讓業(yè)務(wù)代碼變的非常的臃腫。所以 ThinkJS 中提供了非常方便的數(shù)據(jù)校驗(yàn)中間件 think-logic 增加 Logic 層來專門做數(shù)據(jù)校驗(yàn)邏輯,這樣就非常方便的將復(fù)雜的數(shù)據(jù)校驗(yàn)邏輯與業(yè)務(wù)代碼進(jìn)行了解耦。下圖展示了數(shù)據(jù)解析中間件和數(shù)據(jù)校驗(yàn)中間件在整個(gè)架構(gòu)中的位置。

下面我們就常見的請(qǐng)求數(shù)據(jù)類型來說一說在 ThinkJS 中的一些操作過程。

數(shù)據(jù)解析

先回顧一下基礎(chǔ),HTTP 協(xié)議是基于傳輸層 TCP/IP 協(xié)議之上的應(yīng)用層協(xié)議,協(xié)議內(nèi)容是以 ASCII 碼傳輸?shù)模粋€(gè)正常的 HTTP 請(qǐng)求由狀態(tài)碼、請(qǐng)求頭、請(qǐng)求體組成的,像下面這樣:

method> request-url> version>


headers>


entity-body>

HTTP 請(qǐng)求方法中,一般 POST 方法是用來提交數(shù)據(jù)的,協(xié)議規(guī)定 POST 方法提交的數(shù)據(jù)必須放在請(qǐng)求體中,明確數(shù)據(jù)的 MIME 類型,最終 HTTP 請(qǐng)求報(bào)文滿足上面的格式就可以了。數(shù)據(jù)發(fā)送到服務(wù)端后需要成功解析才能使用,像 PHP、Python、Golang、Node.js 的 Web 開發(fā)框架,都實(shí)現(xiàn)了解析數(shù)據(jù)的功能,基本上都是根據(jù)請(qǐng)求頭部 Content-Type 的數(shù)據(jù)類型,然后使用不同的方法進(jìn)行解析,下面介紹四種 POST 提交數(shù)據(jù)的常用 MIME 類型。

application/x-www-form-urlencoded

這是一個(gè)經(jīng)常使用的 MIME 類型,HTML 中

標(biāo)簽如果不帶 enctype 屬性,默認(rèn)就會(huì)用這種數(shù)據(jù)類型提交表單,將數(shù)據(jù)以key1=value1&key2=value2 的形式進(jìn)行編碼,HTTP 報(bào)文大概是下面這樣子:

POST /api/user HTTP/1.1

Content-Type: application/x-www-form-urlencoded


name=ldj&age=16

大部分服務(wù)端語言對(duì)這種類型的數(shù)據(jù)都有很好支持,Node.js 內(nèi)建的模塊 querystring 就可以解析這種數(shù)據(jù)。

application/json

這是目前最常用的請(qǐng)求數(shù)據(jù)類型,尤其是以 Node.js 作為服務(wù)端語言時(shí),前后端統(tǒng)一使用 JSON 數(shù)據(jù)和 JavaScript 能明顯降低程序語言給開發(fā)帶來的復(fù)雜度。前端將 JSON 字符串發(fā)送到服務(wù)端,服務(wù)端解析后變?yōu)閮?nèi)存中的鍵值對(duì)數(shù)據(jù)。HTTP報(bào)文大概是下面這樣子:

POST /api/user HTTP/1.1

Content-Type: application/json;charset=utf-8


{'name':'makeco', 'age': 22}

multipart/form-data

使用表單上傳文件時(shí),需要在 標(biāo)簽上設(shè)置 enctype 的值為 multipart/form-data,可以使用 FormData API 來控制表單數(shù)據(jù),這種類型的 HTTP 報(bào)文就有點(diǎn)復(fù)雜:

POST /api/user HTTP/1.1

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA


------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name='name'


makeco


------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name='avatar'; filename='chrome.png'

Content-Type: image/png


PNG ... content of chrome.png ...

------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

確定 boundary 用來分割不同字段,為了和正文內(nèi)容區(qū)分開,boundary 需要足夠長(zhǎng),接下來字段都是以 --boundary 開始,緊接著是字段描述信息,然后是一個(gè)回車,最后是字段具體內(nèi)容,如果是文件還要加上文件名和文件的 MIME 類型,最后以 --boundary-- 結(jié)束。

text/xml

XML 曾經(jīng)普遍用于前后端交互,和 JSON 相比,這種數(shù)據(jù)格式顯得臃腫,沒有 JSON 數(shù)據(jù)鍵值對(duì)看起來容易。微信公眾號(hào)開發(fā)向服務(wù)器發(fā)送的就是 XML 數(shù)據(jù),看來這種數(shù)據(jù)還在 Web 開發(fā)中占是有一定地位的,HTTP 報(bào)文是這樣的:

POST /user/api HTTP/1.1

Content-Type: text/xml


user>

 name>makeconame>

 age><22 ge="">

user>

以上這些就是常見用于POST請(qǐng)求的數(shù)據(jù)類型,對(duì)應(yīng)到 ThinkJS 中的解析邏輯也非常簡(jiǎn)單。think-payload 中間件負(fù)責(zé)判斷請(qǐng)求數(shù)據(jù)的 MIME 類型,使用對(duì)應(yīng)的解析器將數(shù)據(jù)解析為 JavaScript 對(duì)象,然后掛載到上下文中,最終在 Controller 中被消費(fèi)。如果是請(qǐng)求體數(shù)據(jù) MIME 類型是 multipart/form-data,將會(huì)把文件放到一個(gè)臨時(shí)文件夾中,然后生成一個(gè)包含著該文件基本信息的對(duì)象,也會(huì)掛載到上下文中。和文件上傳有關(guān)的配置請(qǐng)看 think-payload 的文檔,可以在src/config/middleware.js中修改配置。

數(shù)據(jù)校驗(yàn)

ThinkJS 提供了非常強(qiáng)大的校驗(yàn)工具來滿足開發(fā)者的校驗(yàn)需求。根據(jù)用戶提交的數(shù)據(jù)的結(jié)構(gòu)層次,我們一般分為簡(jiǎn)單數(shù)據(jù)結(jié)構(gòu)和復(fù)雜數(shù)據(jù)結(jié)構(gòu)兩種,簡(jiǎn)單數(shù)據(jù)結(jié)構(gòu)的校驗(yàn)大致歸類為三種,分別是存在性校驗(yàn)、數(shù)據(jù)類型校驗(yàn)和合法性校驗(yàn)。如果是復(fù)雜數(shù)據(jù)結(jié)構(gòu),可以自定義全局校驗(yàn)規(guī)則,還可以結(jié)合 ajv 使用 JSON Schema 來進(jìn)行更復(fù)雜的校驗(yàn)。

簡(jiǎn)單數(shù)據(jù)結(jié)構(gòu)

以一個(gè) POST 請(qǐng)求為例,假設(shè)請(qǐng)求體數(shù)據(jù)結(jié)構(gòu)如下:

{

    name: 'maekco',

    age: 22,

    gender: 'female',

    hobbies: ['tea', 'marathon','cooking']

}

在 Logic 中添加的校驗(yàn)規(guī)則:

module.exports = class extends think.Logic {

    postAction() {

        this.rules = {

            name: {

                required: true,

                string: true,

                trim: true,

            },

            age: {

                required: true,

                int: { min: 18, max: 100 },

            },

            gender: {

                required: true,

                in: ['male', 'famale']

            },

            hobbies: {

                required: true,

                array: true,

                children: {

                    string: true,

                    trim: true,

                }

            }

        }

    }

}

這樣保證了之后 Controller 業(yè)務(wù)層一定會(huì)取到 name, age, gender, hobbies 四個(gè)字段(校驗(yàn)不通過則在 Logic 層就返回失?。?name 是字符串,age 是一個(gè) 18-100 的數(shù)字,而 gender 則必須是 male 或 female,hobbies 則必須是字符串?dāng)?shù)組。

Logic 校驗(yàn)除了校驗(yàn)數(shù)據(jù)之外還支持根據(jù)校驗(yàn)規(guī)則回寫校驗(yàn)結(jié)果,比較常見的是請(qǐng)求傳了一個(gè)字符串的數(shù)字進(jìn)來,我們通過 Logic 配置該字段 int: true 則能夠強(qiáng)制將其回寫成整型數(shù)據(jù)。

復(fù)雜數(shù)據(jù)結(jié)構(gòu)

剛才的數(shù)據(jù)中 hobbies 是一個(gè)數(shù)組,我們?yōu)樗性刂付ㄏ嗤?guī)則,如果是數(shù)組的話還好,但是為對(duì)象中的每個(gè)元素指定相同的規(guī)則,很可能不會(huì)滿足實(shí)際需求,如果在上面的數(shù)據(jù)中添加一個(gè) family 數(shù)組,里面每個(gè)對(duì)象都是一位家庭成員,很明顯不能為所有的字段都指定為字符串類型或者是整數(shù)類型,這時(shí)就應(yīng)該使用復(fù)雜數(shù)據(jù)結(jié)構(gòu)的校驗(yàn)方式了。

首先我們?cè)谏弦粋€(gè)的請(qǐng)求體數(shù)據(jù)的基礎(chǔ)上,增加了一個(gè) famliy 對(duì)象數(shù)組。

{

    name: 'maekco',

    age: 22,

    gender: 'female',

    hobbies: ['tea', 'marathon','cooking'],

    family: [

        {

            name: 'xxx',

            age: 45,

            relation: 'mother'

        },

        {

        name: 'xxx',

            age: 15,

            relation: 'sister'

        }

    ]

}

對(duì)于上面數(shù)據(jù)中的name、age、gender、hobbies仍使用 ThinkJS 提供的關(guān)鍵字進(jìn)行校驗(yàn),family數(shù)組需要添加一個(gè)全局校驗(yàn)規(guī)則,結(jié)合 ajv 使用 JSON Schema 定義 family 的數(shù)據(jù)規(guī)則。

{

    'items': {

        'type': 'object',

        'properties': {

            'name': {

                'type': 'string'

        },

        'age': {

            'type': 'number',

        'min': 18,

        'max': 100

        },

        'relation': {

        'enum': ['mother', 'father', 'sister']

        }

    },

    'required': ['name', 'relation'] // 指定必填字段

    },

    'minItems': 0,

    'maxItems': 4

}

接著在 src/config 下面添加 validator.js 文件,并添加如下代碼:

const Ajv = require('ajv');


const ajv = new Ajv({ allErrors: true });

// 編譯json schema文件

const familyValidator = ajv.compile(require('../schema/family.json'));


function genSchemaRule(validator) {

  return function(value, { argName }) {

    const result = validator(value);

    if (result) return true;

    // 如果校驗(yàn)結(jié)果為false,返回出錯(cuò)信息

    return {

      [argName]: ajv.errorsText(validator.errors)

    };

  };

}


module.exports = {

  // 全局規(guī)則

  rules: {

    isFamily: genSchemaRule(familyValidator)

  }

};

補(bǔ)充 Logic 層的校驗(yàn)規(guī)則:

module.exports = class extends think.Logic {

    postAction() {

        this.rules = {

            name: {

                required: true,

                string: true,

                trim: true

            },

            age: {

                required: true,

                int: { min: 18, max: 100 }

            },

            gender: {

                required: true,

                in: ['male', 'female']

            },

            hobbies: {

                required: true,

                array: true,

                children: {

                    string: true,

                    trim: true

                }

            },

            family: {

                required: true,

                isFamily: true

            }

        }

    }

}

使用 Postman 構(gòu)造下面的請(qǐng)求數(shù)據(jù):

{

    'name': 'maekco',

    'age': 22,

    'gender': 'female',

    'hobbies': ['cooking'],

    'family': [

        {

        'name': 'xxx',

        'age': '15', // 應(yīng)該是15,而不是“15”

        'relation': 'sister'

    }

    ]

}

發(fā)送請(qǐng)求后 Logic 層返回如下校驗(yàn)結(jié)果:

{

    'errno': 1001,

    'errmsg': {

        'family': 'data[0].age should be number'

    }

}

如果在family中添加5個(gè)對(duì)象,Logic 層會(huì)返回如下校驗(yàn)結(jié)果:

{

    'errno': 1001,

    'errmsg': {

        'family': 'data should NOT have more than 4 items'

    }

}

上面這種方式是啟動(dòng)時(shí)編譯,使用 JSON Schema 進(jìn)行復(fù)雜數(shù)據(jù)結(jié)構(gòu)校驗(yàn)時(shí),一定要注意編寫標(biāo)準(zhǔn)的 JSON 格式的規(guī)則,否則 ajv 編譯會(huì)出錯(cuò),導(dǎo)致程序無法啟動(dòng)。ThinkJS 官網(wǎng)上提供的JSON Schema 校驗(yàn)方法是運(yùn)行時(shí)動(dòng)態(tài)編譯的,比較消耗性能,而且在 Logic 文件中編寫 JSON 數(shù)據(jù)會(huì)被 prettier 等工具格式化為 JavaScirpt 對(duì)象,導(dǎo)致動(dòng)態(tài)編譯失敗,因此還是推薦使用啟動(dòng)時(shí)編譯的方式創(chuàng)建 JSON Schema 校驗(yàn)。

后記

其實(shí)很多同學(xué)對(duì)數(shù)據(jù)上傳的邏輯只是大概清楚,很多細(xì)節(jié)都不明白,因此經(jīng)常在開發(fā)過程中會(huì)有很多問題。工欲善其事必先利其器,只有把細(xì)節(jié)搞懂了才能應(yīng)對(duì)好每一個(gè)問題。本文也只是淺談了一下數(shù)據(jù)解析和校驗(yàn)相關(guān)的細(xì)節(jié)。

參考:

  • https:///post/four-ways-to-post-data-in-http.html

關(guān)于奇舞周刊

《奇舞周刊》是360公司專業(yè)前端團(tuán)隊(duì)「奇舞團(tuán)」運(yùn)營(yíng)的前端技術(shù)社區(qū)。關(guān)注公眾號(hào)后,直接發(fā)送鏈接到后臺(tái)即可給我們投稿。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多