ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 数据校验 [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; }) ~~~