参数校验
手动校验
之前的参数都是在Controller的入口处,手动的进行校验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | async index() { const {ctx } = this const { query } = ctx.request try { const { type } = query // 缺少参数,没法查 if (!type) { const errMsg = '缺少参数' ctx.response.status = this.config.httpCodeHash.badRequest ctx.response.body = ctx.helper.makeErrorResponse(errMsg) this.logger.error(new Error(errMsg)) return } // 响应内容 const data = await ctx.service.settings.findSettings(type) ctx.response.status = this.config.httpCodeHash.ok ctx.response.body = data } catch (err) { ctx.response.body = err.message || '查询规则错误' ctx.response.status = this.config.httpCodeHash.serverError this.logger.error(err) } } |
这样很很导致大量的代码冗余,每个Controll都要写这样进行校验,如果失败手动返回错误结果(实际上参数校验失败也应该统一处理,后面的异常处理部分会提到)
egg-validate
实际上使用egg-validate插件可以大大简化和标准化参数校验的流程。
安装:
1 | npm i egg-validate --save |
需要在plugin.js
中开启插件:
1 2 3 4 5 | // config/plugin.js exports.validate = { enable: true, package: 'egg-validate', }; |
egg-validate
实际上是由parameter这个库封装而来,它可以针对很多类型的参数进行校验,比如string
、dateTime
、number
、enum
等,具体的使用方法可以参考它的文档。
使用egg-validate
进行参数校验的正确姿势:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 'use strict' const Controller = require('egg').Controller // 创建规则的校验规则 const createRule = { type: { type: 'enum', values: [ 'pre', 'single', 'other' ] }, name: { type: 'string', trim: true }, packageName: { type: 'string', trim: true }, content: { type: 'object' }, } class PrivacyController extends Controller { // 新建预设规则 async create() { const { ctx } = this const { name, packageName, type, content } = ctx.request.body // 参数校验 ctx.validate(createRule, ctx.request.body) // 创建新规则 const data = await ctx.service.settings.createSetting(name.trim(), packageName.trim(), type.trim(), content) // 创建成功 ctx.response.status = this.config.httpCodeHash.created.code ctx.response.body = insertSetting } } module.exports = PrivacyController |
ctx.validate
的第一个参数就是校验的规则,第二个参数是被校验的参数,我们的请求方法是POST,所有的参数都在body
中,所以传入的是ctx.request.body
如果参数校验没有通过,将会抛出一个status
为422
的异常:
这个错误我们没有在Controller中捕获,后面会提到是如何处理的。
要注意的是,在校验规则中,某些类型是可以传入自定义的错误提示信息的,比如对string
的校验,如果使用了formate
选项,那么传入的message
就会有效,其他时刻传入message
无效,无法自定义错误提示信息:
1 2 3 4 | const indexRule = { id: { type: 'string', trim: true, format: /^.{24}$/, message: '非法ID' }, // Mongo生成的ID长度为24位 packageName: { type: 'string', trim: true }, } |
查看它的源码,发现它只有显示或者隐式(type
为email
等)这种情况下才会提示自定义的提示信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | function checkString(rule, value) { if (typeof value !== 'string') { return this.t('should be a string'); } // if required === false, set allowEmpty to true by default if (!rule.hasOwnProperty('allowEmpty') && rule.required === false) { rule.allowEmpty = true; } var allowEmpty = rule.hasOwnProperty('allowEmpty') ? rule.allowEmpty : rule.empty; if (!value) { if (allowEmpty) return; return this.t('should not be empty'); } if (rule.hasOwnProperty('max') && value.length > rule.max) { return this.t('length should smaller than %s', rule.max); } if (rule.hasOwnProperty('min') && value.length < rule.min) { return this.t('length should bigger than %s', rule.min); } if (rule.format && !rule.format.test(value)) { return rule.message || this.t('should match %s', rule.format); } } function checkEnum(rule, value) { if (!Array.isArray(rule.values)) { throw new TypeError('check enum need array type values'); } if (rule.values.indexOf(value) === -1) { return this.t('should be one of %s', rule.values.join(', ')); } } |
有时间想提一个PR,支持所有的类型校验都支持自定义提示信息,但是现在由于无法完全自定义,所以索性在异常处理的时候不对外暴漏具体的message
了,只给出统一的参数校验失败的提示:
1 2 3 4 | { "code": -1, "message": "Validation Failed" } |