當(dāng)在 Action 里處理用戶的請(qǐng)求時(shí),經(jīng)常要先獲取用戶提交過來的數(shù)據(jù),然后對(duì)其校驗(yàn),如果校驗(yàn)沒問題后才能進(jìn)行后續(xù)的操作。當(dāng)參數(shù)校驗(yàn)完成后,有時(shí)候還要進(jìn)行權(quán)限判斷,等這些都判斷無誤后才能進(jìn)行真正的邏輯處理。如果將這些代碼都放在一個(gè) Action 里,勢(shì)必讓 Action 的代碼非常復(fù)雜且冗長(zhǎng)。
為了解決這個(gè)問題, ThinkJS 在控制器前面增加了一層 Logic
,Logic 里的 Action 和控制器里的 Action 一一對(duì)應(yīng),系統(tǒng)在調(diào)用控制器里的 Action 之前會(huì)自動(dòng)調(diào)用 Logic 里的 Action。
Logic 目錄在 src/[module]/logic
,在通過命令 thinkjs controller [name]
創(chuàng)建 Controller 時(shí)會(huì)自動(dòng)創(chuàng)建對(duì)應(yīng)的 Logic。Logic 代碼類似如下:
"use strict";
/**
* logic
* @param {} []
* @return {} []
*/
export default class extends think.logic.base {
/**
* index action logic
* @return {} []
*/
indexAction(){
}
}
其中,Logic 里的 Action 和 Controller 里的 Action 一一對(duì)應(yīng)。Logic 里也支持 __before
和 __after
等魔術(shù)方法。
數(shù)據(jù)校驗(yàn)的配置如下:
export default class extends think.logic.base {
indexAction(){
let rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
配置格式為 字段名
-> 配置
,每個(gè)字段的配置支持多個(gè)校驗(yàn)類型,校驗(yàn)類型之間用 |
隔開,校驗(yàn)類型和參數(shù)之間用 :
隔開,參數(shù)之間用 ,
隔開來支持多個(gè)參數(shù)。
校驗(yàn)類型后面可以接參數(shù),除了支持用逗號(hào)隔開的簡(jiǎn)單參數(shù)外,還可以支持 JSON 格式的復(fù)雜參數(shù)。如:
export default class extends think.logic.base {
indexAction(){
let rules = {
field1: "array|default:[1,2]", //參數(shù)為數(shù)組
field2: "object|default:{\"name\":\"thinkjs\"}" //參數(shù)為對(duì)象
}
}
}
支持的數(shù)據(jù)類型有:boolean
、string
、int
、float
、array
、object
,默認(rèn)為 string
。
使用 default:value
來定義字段的默認(rèn)值,如果當(dāng)前字段值為空,會(huì)將默認(rèn)值覆蓋過去,后續(xù)獲取到的值為該默認(rèn)值。
默認(rèn)根據(jù)當(dāng)前請(qǐng)求的類型來獲取字段對(duì)應(yīng)的值,如果當(dāng)前請(qǐng)求類型是 GET,那么會(huì)通過 this.get('version')
來獲取 version
字段的值。如果請(qǐng)求類型是 POST,那么會(huì)通過 this.post
來獲取字段的值。
但有時(shí)候在 POST 類型下,可能會(huì)獲取上傳的文件或者獲取 URL 上的參數(shù),這時(shí)候就需要指定獲取數(shù)據(jù)的方式了。支持的獲取數(shù)據(jù)方式為 get
,post
和 file
。
export default class extends think.logic.base {
/**
* 保存數(shù)據(jù),POST 請(qǐng)求
* @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
方式來獲取值。
上面的配置只是指定了具體的校驗(yàn)規(guī)則,并沒有指定校驗(yàn)出錯(cuò)后給出的錯(cuò)誤信息。錯(cuò)誤信息支持國(guó)際化,需要在配置文件 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_
+ 校驗(yàn)類型名稱
,值里面支持 {name}
和 {args}
2個(gè)參數(shù),分別代表字段名稱和傳遞的參數(shù)。
如果想定義個(gè)特定字段某個(gè)錯(cuò)誤類型的具體信息,可以通過在后面加上字段名。如:
// 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 錯(cuò)誤信息
}
配置好校驗(yàn)規(guī)則后,可以通過 this.validate
方法進(jìn)行校驗(yàn)。如:
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
方法獲取詳細(xì)的錯(cuò)誤信息。拿到錯(cuò)誤信息后,可以通過this.fail
方法把錯(cuò)誤信息以 JSON 格式輸出,也可以通過 this.display
方法輸出一個(gè)頁(yè)面。
錯(cuò)誤信息通過 errors
字段賦值到模版里,模版里通過下面的方式顯示錯(cuò)誤信息(以 ejs 模版為例):
<%for(var field in errors){%>
<%-field%>:<%errors[field]%>
<%}%>
一般情況下,都是校驗(yàn)有問題后,輸出一個(gè) JSON 信息。如果每次都要在 Logic 的 Action 手動(dòng)調(diào)用this.validate
進(jìn)行校驗(yàn),勢(shì)必比較麻煩。可以通過將校驗(yàn)規(guī)則賦值給 this.rules
屬性進(jìn)行自動(dòng)校驗(yàn)。如:
export default class extends think.logic.base {
indexAction(){
this.rules = {
doc: "string|default:index",
version: "string|in:1.2,2.0|default:2.0"
}
}
}
將校驗(yàn)規(guī)則賦值給 this.rules
屬性后,會(huì)在這個(gè) Action 執(zhí)行完成后自動(dòng)校驗(yàn),如果有錯(cuò)誤則直接輸出 JSON 格式的錯(cuò)誤信息。自動(dòng)校驗(yàn)是通過魔術(shù)方法 __after
來完成的。
必填項(xiàng)。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "required" //name 的值必填
}
}
}
當(dāng)另一個(gè)項(xiàng)的值為某些值其中一項(xiàng)時(shí),該項(xiàng)必填。如:
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredIf:email,admin@example.com,admin1@example.com"
}
}
}
當(dāng) email
的值為 admin@example.com
,admin1@example.com
等其中一項(xiàng)時(shí), name
的值必填。
當(dāng)另一個(gè)項(xiàng)的值不在某些值中時(shí),該項(xiàng)必填。如:
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredNotIf:email,admin@example.com,admin1@example.com"
}
}
}
當(dāng) email
的值不為 admin@example.com
,admin1@example.com
等其中一項(xiàng)時(shí), name
的值必填。
當(dāng)其他幾項(xiàng)有一項(xiàng)值存在時(shí),該項(xiàng)必填。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWith:email,title"
}
}
}
當(dāng) email
, title
等項(xiàng)有一項(xiàng)值存在時(shí),name
的值必填。
當(dāng)其他幾項(xiàng)值都存在時(shí),該項(xiàng)必填。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithAll:email,title"
}
}
}
當(dāng) email
, title
等項(xiàng)值都存在時(shí),name
的值必填。
當(dāng)其他幾項(xiàng)有一項(xiàng)值不存在時(shí),該項(xiàng)必填。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithout:email,title"
}
}
}
當(dāng) email
, title
等項(xiàng)其中有一項(xiàng)值不存在時(shí),name
的值必填。
當(dāng)其他幾項(xiàng)值都存在時(shí),該項(xiàng)必填。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "requiredWithoutAll:email,title"
}
}
}
當(dāng) email
, title
等項(xiàng)值都不存在時(shí),name
的值必填。
值需要包含某個(gè)特定的值。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "contains:thinkjs" //需要包含字符串 thinkjs。
}
}
}
和另一項(xiàng)的值相等。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "equals:firstname"
}
}
}
name
的值需要和 firstname
的值相等。
和另一項(xiàng)的值不等。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "different:firstname"
}
}
}
name
的值不能和 firstname
的值相等。
值需要在一個(gè)日期之后,默認(rèn)為需要在當(dāng)前日期之前。
export default class extends think.logic.base {
indexAction(){
let rules = {
start_time: "before", //需要在當(dāng)前日期之前。
start_time1: "before:2015/10/12 10:10:10" //需要在 2015/10/12 10:10:10 之前。
}
}
}
值需要在一個(gè)日期之后,默認(rèn)為需要在當(dāng)前日期之后。
export default class extends think.logic.base {
indexAction(){
let rules = {
end_time: "after", //需要在當(dāng)前日期之后。
end_time1: "after:2015/10/10" //需要在 2015/10/10 之后。
}
}
}
值只能是 [a-zA-Z] 組成。
export default class extends think.logic.base {
indexAction(){
let rules = {
en_name: "alpha"
}
}
}
en_name
的值只能是 [a-zA-Z] 組成。
值只能是 [a-zA-Z_] 組成。
值只能是 [a-zA-Z0-9] 組成。
值只能是 [a-zA-Z0-9_] 組成。
值只能是 ascii 字符組成。
值必須是 base64 編碼。
字節(jié)長(zhǎng)度需要在一個(gè)區(qū)間內(nèi)。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "byteLength:10" //字節(jié)長(zhǎng)度不能小于 10
name1: "byteLength:10,100" //字節(jié)長(zhǎng)度需要在 10 - 100 之間
}
}
}
需要是信用卡數(shù)字。
需要是貨幣。
需要是個(gè)日期。
需要是個(gè)小數(shù)。
需要被一個(gè)數(shù)整除。
export default class extends think.logic.base {
indexAction(){
let rules = {
count: "divisibleBy:3" //可以被 3 整除
}
}
}
需要是個(gè) email 格式。
需要是個(gè)合格的域名。
需要是個(gè)浮點(diǎn)數(shù)。
export default class extends think.logic.base {
indexAction(){
let rules = {
money: "float" //需要是個(gè)浮點(diǎn)數(shù)
money1: "float:3.2" //需要是個(gè)浮點(diǎn)數(shù),且最小值為 3.2
money2: "float:3.2,10.5" //需要是個(gè)浮點(diǎn)數(shù),且最小值為 3.2,最大值為 10.5
}
}
}
包含寬字節(jié)字符。
包含半字節(jié)字符。
需要是個(gè)十六進(jìn)制顏色值。
需要是十六進(jìn)制。
需要是 ip 格式。
需要是 ip4 格式。
需要是 ip6 格式。
需要是圖書編碼。
需要是證券識(shí)別編碼。
需要是 iso8601 日期格式。
在某些值中。
export default class extends think.logic.base {
indexAction(){
let rules = {
version: "in:1.2,2.0" //需要是 1.2,2.0 其中一個(gè)
}
}
}
不能在某些值中。
export default class extends think.logic.base {
indexAction(){
let rules = {
version: "noin:1.2,2.0" //不能是 1.2,2.0 其中一個(gè)
}
}
}
需要是 int 型。
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "int" //需要是 int 型
value1: "int:1" //不能小于1
value2: "int:10,100" //需要在 10 - 100 之間
}
}
}
不能小于某個(gè)值。
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "min:10" //不能小于10
}
}
}
不能大于某個(gè)值。
export default class extends think.logic.base {
indexAction(){
let rules = {
value: "max:10" //不能大于10
}
}
}
長(zhǎng)度需要在某個(gè)范圍。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "length:10" //長(zhǎng)度不能小于10
name1: "length:10,100" //長(zhǎng)度需要在 10 - 100 之間
}
}
}
長(zhǎng)度不能小于最小長(zhǎng)度。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "minLength:10" //長(zhǎng)度不能小于10
}
}
}
長(zhǎng)度不能大于最大長(zhǎng)度。
export default class extends think.logic.base {
indexAction(){
let rules = {
name: "maxLength:10" //長(zhǎng)度不能大于10
}
}
}
需要都是小寫字母。
需要都是大小字母。
需要手機(jī)號(hào)。
export default class extends think.logic.base {
indexAction(){
let rules = {
mobile: "mobile:zh-CN" //必須為中國(guó)的手機(jī)號(hào)
}
}
}
是 MongoDB 的 ObjectID。
包含多字節(jié)字符。
是個(gè) url。
數(shù)據(jù)庫(kù)查詢 order,如:name DESC。
數(shù)據(jù)庫(kù)查詢的字段,如:name,title。
上傳的文件是否是個(gè)圖片。
以某些字符打頭。
以某些字符結(jié)束。
值為字符串。
值為數(shù)組。
值為布爾類型。
值為對(duì)象。
如果默認(rèn)支持的校驗(yàn)類型不能滿足需求,可以通過 think.validate
方法對(duì)校驗(yàn)類型進(jìn)行擴(kuò)展。如:
// src/common/bootstrap/validate.js
think.validate("validate_name", (value, ...args) => {
//需要返回 true 或者 false
//true 表示校驗(yàn)成功,false 表示校驗(yàn)失敗
})
上面注冊(cè)了一個(gè)名為 validate_name
的校驗(yàn)類型,這樣在 Logic 里就可以直接使用該校驗(yàn)類型了。
如果要解析后面的 args
,如:該字段值跟其他字段值進(jìn)行比較,這時(shí)拿到的參數(shù)是其他字段名稱,但比較的時(shí)候肯定需要拿到這個(gè)字段值,所以需要將字段名稱解析為對(duì)應(yīng)的字段值。
可以通過注冊(cè)一個(gè)解析參數(shù)函數(shù)來完成。如:上面的校驗(yàn)類型名稱為 validate_name
,那么對(duì)應(yīng)的解析參數(shù)的名稱必須為 _validate_name
,即:_
+ 校驗(yàn)類型
。
think.validate("_validate_name", (args, data) => {
let arg0 = args[0];
args[0] = data[arg0].value; //將第一個(gè)參數(shù)字段名稱解析為對(duì)應(yīng)的參數(shù)值
return args;
})
更多建議: