💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ### egg 中使用 BetterValidate >[danger] BetterValidate 依赖于 validator 插件 文档:[https://www.npmjs.com/package/validator](https://www.npmjs.com/package/validator) 1. app.js 构建 validate 目录 并且挂载到ctx ``` const path = require('path'); async didLoad() { // 1.全局异常处理 const { HttpExceptions } = require('./app/exceptions/http_exceptions') global.myErrors = HttpExceptions; // 2. betterValidate 挂载 ctx const validatorsPaths = this.app.loader.getLoadUnits().map(unit => path.join(unit.path, 'app/validators')); this.app.loader.loadToContext(validatorsPaths, 'validators', { call: true, fieldClass: 'validatorsClasses', }); } ``` ***** 2. 建立 2个 BetterValidate 核心类库 * [ ] app / cores / utils.js ``` const findMembers = function (instance, { prefix, specifiedType, filter }) { // 递归函数 function _find(instance) { //基线条件(跳出递归) if (instance.__proto__ === null) return [] let names = Reflect.ownKeys(instance) names = names.filter((name) => { // 过滤掉不满足条件的属性或方法名 return _shouldKeep(name) }) return [...names, ..._find(instance.__proto__)] } function _shouldKeep(value) { if (filter) { if (filter(value)) { return true } } if (prefix) if (value.startsWith(prefix)) return true if (specifiedType) if (instance[value] instanceof specifiedType) return true } return _find(instance) } module.exports = { findMembers } ``` ***** * [ ] app / cores / valitators.js ``` const validator = require('validator') const { HttpExceptions } = require('../exceptions/http_exceptions') const { get, last, set, cloneDeep } = require("lodash") const { findMembers } = require('./utils') class BetterValidate { constructor() { this.data = {} this.parsed = {} } _assembleAllParams(ctx) { return { body: ctx.request.body, query: ctx.request.query, path: ctx.params, header: ctx.request.header } } get(path, parsed = true) { if (parsed) { const value = get(this.parsed, path, null) if (value == null) { const keys = path.split('.') const key = last(keys) return get(this.parsed.default, key) } return value } else { return get(this.data, path) } } _findMembersFilter(key) { if (/validate([A-Z])\w+/g.test(key)) { return true } if (this[key] instanceof Array) { this[key].forEach(value => { const isRuleType = value instanceof Rule if (!isRuleType) { throw new Error('验证数组必须全部为Rule类型') } }) return true } return false } async validate(ctx, alias = {}) { this.alias = alias let params = this._assembleAllParams(ctx) this.data = cloneDeep(params) this.parsed = cloneDeep(params) const memberKeys = findMembers(this, { filter: this._findMembersFilter.bind(this) }) const errorMsgs = [] // const map = new Map(memberKeys) for (let key of memberKeys) { const result = await this._check(key, alias) if (!result.success) { errorMsgs.push(result.msg) } } if (errorMsgs.length != 0) { throw new HttpExceptions(errorMsgs) } ctx.v = this return this } async _check(key, alias = {}) { const isCustomFunc = typeof (this[key]) == 'function' ? true : false let result; if (isCustomFunc) { try { await this[key](this.data) result = new RuleResult(true) } catch (error) { result = new RuleResult(false, error.msg || error.message || '参数错误') } // 函数验证 } else { // 属性验证, 数组,内有一组Rule const rules = this[key] const ruleField = new RuleField(rules) // 别名替换 key = alias[key] ? alias[key] : key const param = this._findParam(key) result = ruleField.validate(param.value) if (result.pass) { // 如果参数路径不存在,往往是因为用户传了空值,而又设置了默认值 if (param.path.length == 0) { set(this.parsed, ['default', key], result.legalValue) } else { set(this.parsed, param.path, result.legalValue) } } } if (!result.pass) { const msg = `${isCustomFunc ? '' : key}${result.msg}` return { msg: msg, success: false } } return { msg: 'ok', success: true } } _findParam(key) { let value value = get(this.data, ['query', key]) if (value) { return { value, path: ['query', key] } } value = get(this.data, ['body', key]) if (value) { return { value, path: ['body', key] } } value = get(this.data, ['path', key]) if (value) { return { value, path: ['path', key] } } value = get(this.data, ['header', key]) if (value) { return { value, path: ['header', key] } } return { value: null, path: [] } } } class RuleResult { constructor(pass, msg = '') { Object.assign(this, { pass, msg }) } } class RuleFieldResult extends RuleResult { constructor(pass, msg = '', legalValue = null) { super(pass, msg) this.legalValue = legalValue } } class Rule { constructor(name, msg, ...params) { Object.assign(this, { name, msg, params }) } validate(field) { if (this.name == 'isOptional') return new RuleResult(true) if (!validator[this.name](field + '', ...this.params)) { return new RuleResult(false, this.msg || this.message || '参数错误') } return new RuleResult(true, '') } } class RuleField { constructor(rules) { this.rules = rules } validate(field) { if (field == null) { // 如果字段为空 const allowEmpty = this._allowEmpty() const defaultValue = this._hasDefault() if (allowEmpty) { return new RuleFieldResult(true, '', defaultValue) } else { return new RuleFieldResult(false, '字段是必填参数') } } const filedResult = new RuleFieldResult(false) for (let rule of this.rules) { let result = rule.validate(field) if (!result.pass) { filedResult.msg = result.msg filedResult.legalValue = null // 一旦一条校验规则不通过,则立即终止这个字段的验证 return filedResult } } return new RuleFieldResult(true, '', this._convert(field)) } _convert(value) { for (let rule of this.rules) { if (rule.name == 'isInt') { return parseInt(value) } if (rule.name == 'isFloat') { return parseFloat(value) } if (rule.name == 'isBoolean') { return value ? true : false } } return value } _allowEmpty() { for (let rule of this.rules) { if (rule.name == 'isOptional') { return true } } return false } _hasDefault() { for (let rule of this.rules) { const defaultValue = rule.params[0] if (rule.name == 'isOptional') { return defaultValue } } } } module.exports = { Rule, BetterValidate } ``` ***** 3. 使用BetterValidate 进行参数验证 app / validators / user / register.js * [ ] 这里进行User模块的注册验证 ``` const { BetterValidate, Rule } = require('../../cores/valitators'); /** * 用户注册校验 */ class Register extends BetterValidate { constructor() { super() this.email = [ new Rule('isEmail', '不符合Email规范') ], this.password = [ new Rule('isLength', '密码至少6个字符,最多32个字符', { min: 6, max: 32 }), new Rule('matches', '密码必须是字母和数字组合', '^(?![0-9]+$)(?![a-zA-Z]+$)[0-9a-zA-Z]') ], this.passwordConfirm = this.password, this.nickname = [ new Rule('isLength', '昵称最少2个字符,最大6个字符', {min:2,max:30}) ] } /** * 自定义验证规则 * @param { String } value POST表单提交过来的值 */ validatePassword(value) { // body.password 表单提交的 password 字段 const pwd = value.body.password; const pwdConfirm = value.body.passwordConfirm; if (!Object.is(pwd,pwdConfirm)) { throw new Error('两次密码输入不一致!') } } } module.exports = Register; ``` ***** ### 更多验证规则 ``` const { BetterValidate, Rule } = require('../../cores/valitators'); /** * 用户登录校验 */ class Login extends BetterValidate { constructor() { super() this.account = [ new Rule('isLength', '账号最少4位,不能超过32位', { min: 4, max: 32 }) ], this.secret = [ // 存在则验证,不存在则不验证 new Rule('isOptional'), new Rule('isLength', '至少6个字符', { min: 6, max: 128 }) ] } /** * 自定义验证规则 * @param { String } value POST表单提交过来的值 */ validateType(value) { const type = value.body.type; if (!type) throw new Error('type 不能为空!'); if (!this._checkTypes(type)) throw new Error('type类型必须为 100 101 102 200'); } // 自定义枚举类型 _checkTypes(type) { const allowed = { USER_MINI_PROGRAM: 100, USER_EMAIL: 101, USER_MOBILE: 102, ADMIN_EMAIL: 200, WEIXIN_LOGIN:300 } for (let [key, val] of Object.entries(allowed)) { if ( val === Number(type) ) { return true; } } return false; } } module.exports = Login; ``` ***** ### 控制器进行参数验证 ~~~ // 使用 const v = await new RegisterValidator().validate(ctx); // 取数据 const nickname = v.get("body.nickname"); await userDao.createUser(ctx, v); ctx.json( new Success({ msg: "用户创建成功" }) ); ~~~