# 數據校驗
[TOC=2,3]
當在 Action 里處理用戶的請求時,經常要先獲取用戶提交過來的數據,然后對其校驗,如果校驗沒問題后才能進行后續的操作。當參數校驗完成后,有時候還要進行權限判斷,等這些都判斷無誤后才能進行真正的邏輯處理。如果將這些代碼都放在一個 Action 里,勢必讓 Action 的代碼非常復雜且冗長。
為了解決這個問題, ThinkJS 在控制器前面增加了一層?`Logic`,Logic 里的 Action 和控制器里的 Action 一一對應,系統在調用控制器里的 Action 之前會自動調用 Logic 里的 Action。
## Logic 層
Logic 目錄在?`src/[module]/logic`,在通過命令?`thinkjs controller [name]`?創建 Controller 時會自動創建對應的 Logic。Logic 代碼類似如下:
~~~
"use strict";
/**
* logic
* @param {} []
* @return {} []
*/
export default class extends think.logic.base {
/**
* index action logic
* @return {} []
*/
indexAction(){
}
}
~~~
其中,Logic 里的 Action 和 Controller 里的 Action 一一對應。Logic 里也支持?`__before`?和?`__after`?等魔術方法。
## 數據校驗配置
數據校驗的配置如下:
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
~~~
### 配置格式
配置格式為?`字段名`?->?`配置`,每個字段的配置支持多個校驗類型,校驗類型之間用?`|`?隔開,校驗類型和參數之間用?`:`?隔開,參數之間用?`,`?隔開來支持多個參數。
### 參數格式
校驗類型后面可以接參數,除了支持用逗號隔開的簡單參數外,還可以支持 JSON 格式的復雜參數。如:
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
field1: "array|default:[1,2]", //參數為數組
field2: "object|default:{\"name\":\"thinkjs\"}" //參數為對象
}
}
}
~~~
### 支持的數據類型
支持的數據類型有:`boolean`、`string`、`int`、`float`、`array`、`object`,默認為?`string`。
### 默認值
使用?`default:value`?來定義字段的默認值,如果當前字段值為空,會將默認值覆蓋過去,后續獲取到的值為該默認值。
### 獲取數據的方式
默認根據當前請求的類型來獲取字段對應的值,如果當前請求類型是 GET,那么會通過?`this.get('version')`來獲取?`version`?字段的值。如果請求類型是 POST,那么會通過?`this.post`?來獲取字段的值。
但有時候在 POST 類型下,可能會獲取上傳的文件或者獲取 URL 上的參數,這時候就需要指定獲取數據的方式了。支持的獲取數據方式為?`get`,`post`?和?`file`。
~~~
export default class extends think.logic.base {
/**
* 保存數據,POST 請求
* @return {} []
*/
saveAction(){
let rules = {
name: "required",
image: "object|file|required",
version: "string|get|in:1.2,2.0|default:2.0"
}
}
}
~~~
上面示例指定了字段?`name`?通過?`post`?方法來獲取值,字段?`image`?通過?`file`?方式來獲取值,字段?`version`通過?`get`?方式來獲取值。
### 錯誤信息
上面的配置只是指定了具體的校驗規則,并沒有指定校驗出錯后給出的錯誤信息。錯誤信息支持國際化,需要在配置文件?`src/common/config/locale/[lang].js`?中定義。如:
~~~
// src/common/config/locale/en.js
export default {
validate_required: "{name} can not be blank",
validate_contains: "{name} need contains {args}",
}
~~~
其中 key 為?`validate_`?+?`校驗類型名稱`,值里面支持?`{name}`?和?`{args}`?2個參數,分別代表字段名稱和傳遞的參數。
如果想定義個特定字段某個錯誤類型的具體信息,可以通過在后面加上字段名。如:
~~~
// src/common/config/locale/en.js
export default {
validate_required: "{name} can not be blank",
validate_required_email: "email can not be blank", //指定字段 email 的 required 錯誤信息
}
~~~
## 數據校驗方法
配置好校驗規則后,可以通過?`this.validate`?方法進行校驗。如:
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
let flag = this.validate(rules);
if(!flag){
return this.fail("validate error", this.errors());
}
}
}
~~~
如果返回值為?`false`,那么可以通過?`this.errors`?方法獲取詳細的錯誤信息。拿到錯誤信息后,可以通過`this.fail`?方法把錯誤信息以 JSON 格式輸出,也可以通過?`this.display`?方法輸出一個頁面。
錯誤信息通過?`errors`?字段賦值到模版里,模版里通過下面的方式顯示錯誤信息(以 ejs 模版為例):
~~~
<%for(var field in errors){%>
<%-field%>:<%errors[field]%>
<%}%>
~~~
#### 自動校驗
一般情況下,都是校驗有問題后,輸出一個 JSON 信息。如果每次都要在 Logic 的 Action 手動調用`this.validate`?進行校驗,勢必比較麻煩。可以通過將校驗規則賦值給?`this.rules`?屬性進行自動校驗。如:
~~~
export default class extends think.logic.base {
indexAction(){
this.rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
~~~
將校驗規則賦值給?`this.rules`?屬性后,會在這個 Action 執行完成后自動校驗,如果有錯誤則直接輸出 JSON 格式的錯誤信息。自動校驗是通過魔術方法?`__after`?來完成的。
## 支持的校驗類型
### required
必填項。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "required" //name 的值必填
}
}
}
~~~
### requiredIf
當另一個項的值為某些值其中一項時,該項必填。如:
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredIf:email,admin@example.com,admin1@example.com"
}
}
}
~~~
當?`email`?的值為?`admin@example.com`,`admin1@example.com`?等其中一項時,?`name`?的值必填。
### requiredNotIf
當另一個項的值不在某些值中時,該項必填。如:
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredNotIf:email,admin@example.com,admin1@example.com"
}
}
}
~~~
當?`email`?的值不為?`admin@example.com`,`admin1@example.com`?等其中一項時,?`name`?的值必填。
### requiredWith
當其他幾項有一項值存在時,該項必填。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWith:email,title"
}
}
}
~~~
當?`email`,?`title`?等項有一項值存在時,`name`?的值必填。
### requiredWithAll
當其他幾項值都存在時,該項必填。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithAll:email,title"
}
}
}
~~~
當?`email`,?`title`?等項值都存在時,`name`?的值必填。
### requiredWithout
當其他幾項有一項值不存在時,該項必填。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithout:email,title"
}
}
}
~~~
當?`email`,?`title`?等項其中有一項值不存在時,`name`?的值必填。
### requiredWithoutAll
當其他幾項值都存在時,該項必填。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithoutAll:email,title"
}
}
}
~~~
當?`email`,?`title`?等項值都不存在時,`name`?的值必填。
### contains
值需要包含某個特定的值。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "contains:thinkjs" //需要包含字符串 thinkjs。
}
}
}
~~~
### equals
和另一項的值相等。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "equals:firstname"
}
}
}
~~~
`name`?的值需要和?`firstname`?的值相等。
### different
和另一項的值不等。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "different:firstname"
}
}
}
~~~
`name`?的值不能和?`firstname`?的值相等。
### before
值需要在一個日期之后,默認為需要在當前日期之前。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
start_time: "before", //需要在當前日期之前。
start_time1: "before:2015/10/12 10:10:10" //需要在 2015/10/12 10:10:10 之前。
}
}
}
~~~
### after
值需要在一個日期之后,默認為需要在當前日期之后。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
end_time: "after", //需要在當前日期之后。
end_time1: "after:2015/10/10" //需要在 2015/10/10 之后。
}
}
}
~~~
### alpha
值只能是 [a-zA-Z] 組成。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
en_name: "alpha"
}
}
}
~~~
`en_name`?的值只能是 [a-zA-Z] 組成。
### alphaDash
值只能是 [a-zA-Z_] 組成。
### alphaNumeric
值只能是 [a-zA-Z0-9] 組成。
### alphaNumericDash
值只能是 [a-zA-Z0-9_] 組成。
### ascii
值只能是 ascii 字符組成。
### base64
值必須是 base64 編碼。
### byteLength
字節長度需要在一個區間內。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "byteLength:10" //字節長度不能小于 10
name1: "byteLength:10,100" //字節長度需要在 10 - 100 之間
}
}
}
~~~
### creditcard
需要是信用卡數字。
### currency
需要是貨幣。
### date
需要是個日期。
### decimal
需要是個小數。
### divisibleBy
需要被一個數整除。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
count: "divisibleBy:3" //可以被 3 整除
}
}
}
~~~
### email
需要是個 email 格式。
### fqdn
需要是個合格的域名。
### float
需要是個浮點數。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
money: "float" //需要是個浮點數
money1: "float:3.2" //需要是個浮點數,且最小值為 3.2
money2: "float:3.2,10.5" //需要是個浮點數,且最小值為 3.2,最大值為 10.5
}
}
}
~~~
### fullWidth
包含寬字節字符。
### halfWidth
包含半字節字符。
### hexColor
需要是個十六進制顏色值。
### hex
需要是十六進制。
### ip
需要是 ip 格式。
### ip4
需要是 ip4 格式。
### ip6
需要是 ip6 格式。
### isbn
需要是圖書編碼。
#### isin
需要是證券識別編碼。
### iso8601
需要是 iso8601 日期格式。
### in
在某些值中。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
version: "in:1.2,2.0" //需要是 1.2,2.0 其中一個
}
}
}
~~~
### noin
不能在某些值中。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
version: "noin:1.2,2.0" //不能是 1.2,2.0 其中一個
}
}
}
~~~
### int
需要是 int 型。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "int" //需要是 int 型
value1: "int:1" //不能小于1
value2: "int:10,100" //需要在 10 - 100 之間
}
}
}
~~~
### min
不能小于某個值。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "min:10" //不能小于10
}
}
}
~~~
### max
不能大于某個值。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "max:10" //不能大于10
}
}
}
~~~
### length
長度需要在某個范圍。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "length:10" //長度不能小于10
name1: "length:10,100" //長度需要在 10 - 100 之間
}
}
}
~~~
### minLength
長度不能小于最小長度。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "minLength:10" //長度不能小于10
}
}
}
~~~
### maxLength
長度不能大于最大長度。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "maxLength:10" //長度不能大于10
}
}
}
~~~
### lowercase
需要都是小寫字母。
### uppercase
需要都是大小字母。
### mobile
需要手機號。
~~~
export default class extends think.logic.base {
indexAction(){
let rules = {
mobile: "mobile:zh-CN" //必須為中國的手機號
}
}
}
~~~
### mongoId
是 MongoDB 的 ObjectID。
### multibyte
包含多字節字符。
### url
是個 url。
### order
數據庫查詢 order,如:name DESC。
### field
數據庫查詢的字段,如:name,title。
### image
上傳的文件是否是個圖片。
### startWith
以某些字符打頭。
### endWith
以某些字符結束。
### string
值為字符串。
### array
值為數組。
### boolean
值為布爾類型。
### object
值為對象。
## 擴展校驗類型
如果默認支持的校驗類型不能滿足需求,可以通過?`think.validate`?方法對校驗類型進行擴展。如:
~~~
// src/common/bootstrap/validate.js
think.validate("validate_name", (value, ...args) => {
//需要返回 true 或者 false
//true 表示校驗成功,false 表示校驗失敗
})
~~~
上面注冊了一個名為?`validate_name`?的校驗類型,這樣在 Logic 里就可以直接使用該校驗類型了。
#### 參數解析
如果要解析后面的?`args`,如:該字段值跟其他字段值進行比較,這時拿到的參數是其他字段名稱,但比較的時候肯定需要拿到這個字段值,所以需要將字段名稱解析為對應的字段值。
可以通過注冊一個解析參數函數來完成。如:上面的校驗類型名稱為?`validate_name`,那么對應的解析參數的名稱必須為?`_validate_name`,即:`_`?+?`校驗類型`。
~~~
think.validate("_validate_name", (args, data) => {
let arg0 = args[0];
args[0] = data[arg0].value; //將第一個參數字段名稱解析為對應的參數值
return args;
})
~~~
- 快速入門
- 介紹
- 創建項目
- 項目結構
- 代碼規范
- 升級指南
- 進階應用
- 模塊
- 控制器
- 視圖
- 配置
- 路由
- 模型
- 介紹
- 事務
- 關聯模型
- Mysql
- MongoDB
- SQLite
- Adapter
- 介紹
- Cache
- Session
- WebSocket
- Template
- 擴展功能
- thinkjs 命令
- 靜態資源訪問
- Middleware
- Service
- Cookie
- 錯誤處理
- 錯誤信息
- 數據校驗
- 國際化
- 路徑常量
- REST API
- 定時任務
- 線上部署
- 推薦模塊
- API
- think
- think.base
- think.http.base
- http
- controller
- rest controller
- model
- model.mongo
- middleware