Node在Controller層進行數(shù)據(jù)校驗的過程詳解
前言
幽默風(fēng)趣的后端程序員一般自嘲為 CURD Boy。CURD, 也就是對某一存儲資源的增刪改查,這完全是面向數(shù)據(jù)編程啊。
真好呀,面向數(shù)據(jù)編程,往往會對業(yè)務(wù)理解地更加透徹,從而寫出更高質(zhì)量的代碼,造出更少的 BUG。既然是面向數(shù)據(jù)編程那更需要避免臟數(shù)據(jù)的出現(xiàn),加強數(shù)據(jù)校驗。否則,難道要相信前端的數(shù)據(jù)校驗嗎,畢竟前端數(shù)據(jù)校驗直達用戶,是為了 UI 層更友好的用戶反饋。
數(shù)據(jù)校驗層
后端由于重業(yè)務(wù)邏輯以及待處理各種數(shù)據(jù),以致于分成各種各樣的層級,以我經(jīng)歷過的后端項目就有分為 Controller、Service、Model、Helper、Entity 等各種命名的層,五花八門。但這里肯定有一個層稱為 Controller,站在后端最上層直接接收客戶端傳輸數(shù)據(jù)。
由于 Controller 層是服務(wù)器端中與客戶端數(shù)據(jù)交互的最頂層,秉承著 Fail Fast 的原則,肩負著數(shù)據(jù)過濾器的功能,對于不合法數(shù)據(jù)直接打回去,如同秦瓊與尉遲恭門神般威嚴(yán)。
數(shù)據(jù)校驗同時衍生了一個半文檔化的副產(chǎn)品,你只需要看一眼數(shù)據(jù)校驗層,便知道要傳哪些字段,都是些什么格式。
以下都是常見的數(shù)據(jù)校驗,本文講述如何對它們進行校驗:
- required/optional
- 基本的數(shù)據(jù)校驗,如 number、string、timestamp 及值需要滿足的條件
- 復(fù)雜的數(shù)據(jù)校驗,如 IP、手機號、郵箱與域名
const body = {
id,
name,
mobilePhone,
email
}
山月接觸過一個沒有數(shù)據(jù)校驗層的后端項目,if/else 充斥在各種層級,萬分痛苦,分分鐘向重構(gòu)。
JSON Schema
JSON Schema 基于 JSON 進行數(shù)據(jù)校驗格式,并附有一份規(guī)范 json-schema.org,目前 (2020-08) 最新版本是 7.0。各種服務(wù)器編程語言都對規(guī)范進行了實現(xiàn),如 go、java、php 等,當(dāng)然偉大的 javascript 也有,如不溫不火的ajv。
以下是校驗用戶信息的一個 Schema,可見語法復(fù)雜與繁瑣:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "User",
"description": "用戶信息",
"type": "object",
"properties": {
"id": {
"description": "用戶 ID",
"type": "integer"
},
"name": {
"description": "用戶姓名",
"type": "string"
},
"email": {
"description": "用戶郵箱",
"type": "string",
"format": "email",
"maxLength": 20
},
"mobilePhone": {
"description": "用戶手機號",
"type": "string",
"pattern": "^(?:(?:\+|00)86)?1[3-9]\d{9}$",
"maxLength": 15
}
},
"required": ["id", "name"]
}
對于復(fù)雜的數(shù)據(jù)類型校驗,JSON Schema 內(nèi)置了以下 Format,方便快捷校驗
- Dates and times
- Email addresses
- Hostnames
- IP Addresses
- Resource identifiers
- URI template
- JSON Pointer
- Regular Expressions
對于不在內(nèi)置 Format 中的手機號,使用 ajv.addFormat 可手動添加 Format
ajv.addFormat('mobilePhone', (str) => /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(str));
Joi
joi 自稱最強大的 JS 校驗庫,在 github 也斬獲了一萬六顆星星。相比 JSON Schema 而言,它的語法更加簡潔并且功能強大。
The most powerful data validation library for JS
完成相同的校驗,僅需要更少的代碼,并能夠完成更加強大的校驗。以下僅做示例,更多示例請前往文檔。
const schema = Joi.object({
id: Joi.number().required(),
name: Joi.number().required(),
email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }),
mobilePhone: Joi.string().pattern(/^(?:(?:\+|00)86)?1[3-9]\d{9}$/),
password: Joi.string().pattern(/^[a-zA-Z0-9]{3,30}$/),
// 與 password 相同的校驗
repeatPassword: Joi.ref('password'),
})
// 密碼與重復(fù)密碼需要同時發(fā)送
.with('password', 'repeat_password');
// 郵箱與手機號提供一個即可
.xor('email', 'mobilePhone')
數(shù)據(jù)校驗與路由層集成
由于數(shù)據(jù)直接從路由傳遞,因此 koajs 官方基于 joi 實現(xiàn)了一個joi-router,前置數(shù)據(jù)校驗到路由層,對前端傳遞來的 query、body 與 params 進行校驗。
joi-router 也同時基于 co-body 對前端傳輸?shù)母鞣N content-type 進行解析及限制。如限制為 application/json,也可在一定程度上防止 CSRF 攻擊。
const router = require('koa-joi-router');
const public = router();
public.route({
method: 'post',
path: '/signup',
validate: {
header: joiObject,
query: joiObject,
params: joiObject,
body: joiObject,
maxBody: '64kb',
output: { '400-600': { body: joiObject } },
type: 'json',
failure: 400,
continueOnError: false
},
pre: async (ctx, next) => {
await checkAuth(ctx);
return next();
},
handler: async (ctx) => {
await createUser(ctx.request.body);
ctx.status = 201;
},
});
正則表達式與安全正則表達式
山月在一次排查性能問題時發(fā)現(xiàn),一條 API 竟在數(shù)據(jù)校驗層耗時過久,這是我未曾想到的。而問題根源在于不安全的正則表達式,那什么叫做不安全的正則表達式呢?
比如下邊這個能把 CPU 跑掛的正則表達式就是一個定時炸彈,回溯次數(shù)進入了指數(shù)爆炸般的增長。
可以參考文章 淺析 ReDos 原理與實踐
const safe = require('safe-regex')
const re = /(x+x+)+y/
// 能跑死 CPU 的一個正則
re.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
// 使用 safe-regex 判斷正則是否安全
safe(re) // false
數(shù)據(jù)校驗,針對的大多是字符串校驗,也會充斥著各種各樣的正則表達式,保證正則表達式的安全相當(dāng)緊要。safe-regex 能夠發(fā)現(xiàn)哪些不安全的正則表達式。
總結(jié)
- Controller 層需要進行統(tǒng)一的數(shù)據(jù)校驗,可以采用 JSON Schema (Node 實現(xiàn) ajv) 與 Joi
- JSON Schema 有官方規(guī)范及各個語言的實現(xiàn),但語法繁瑣,可使用校驗功能更為強大的 Joi
- 進行字符串校驗時,注意不安全的正則引起的性能問題
到此這篇關(guān)于Node在Controller層進行數(shù)據(jù)校驗的文章就介紹到這了,更多相關(guān)Node在Controller層數(shù)據(jù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Node.js調(diào)試技術(shù)總結(jié)分享
Node.js是一個可以快速構(gòu)建網(wǎng)絡(luò)服務(wù)及應(yīng)用的平臺。該平臺的構(gòu)建是基于Chrome's JavaScript runtime,也就是說,實際上它是對Google V8引擎(應(yīng)用于Google Chrome瀏覽器)進行了封裝。 今天介紹Node.js調(diào)式目前有幾種技術(shù),需要的朋友可以參考下。2017-03-03
NodeJS與HTML5相結(jié)合實現(xiàn)拖拽多個文件上傳到服務(wù)器的實現(xiàn)方法
這篇文章主要介紹了NodeJS與HTML5相結(jié)合實現(xiàn)拖拽多個文件上傳到服務(wù)器的實現(xiàn)方法的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-07-07

