目前 egg-validate 底层是 parameter,有固定的 rule 格式,但并不是标准的 jsonSchema 校验,某些校验场景比较复杂的情况,parameter 的能力无法满足
要校验的 json
[{
"name": "1",
"status": "success"
}, {
"name": "2",
"status": "failed",
"failedMessage": "reason"
}]
校验需求
当 status=failed 时,必须有 failedMessage 这个字段,status=success,必然没有 failedMessage 这个字段
这时基于 parameter 的特性就无法直接通过定义 rule 来满足,而是要组合程序逻辑去分别 validate
但是通过 jsonSchema 就能直接定义出来
{
"type": "array",
"item": {
"oneOf": [{
"type": "object",
"properties" : {
"name": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": ["name", "status"]
}, {
"type": "object",
"properties" : {
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"failedMessage": {
"type": "string"
}
},
"required": ["name", "status", "failedMessage"]
}]
}
}
引入 ajv,目前社区最好的 jsonSchema validate 库。http://epoberezkin.github.io/ajv/
在 egg-validate 插件,ctx 注入额外的方法
// app/extend/context.js
'use strict';
module.exports = {
/**
* validate data with rules
*
* @param {Object} rules - validate rule object, see [parameter](https://github.com/node-modules/parameter)
* @param {Object} [data] - validate target, default to `this.request.body`
*/
validate(rules, data) {
data = data || this.request.body;
const errors = this.app.validator.validate(rules, data);
if (errors) {
this.throw(422, 'Validation Failed', {
code: 'invalid_param',
errors,
});
}
},
+ jsonSchemaValidate(jsonSchema, data) {
+ data = data || this.request.body;
+ const errors = this.app.jsonSchemaValidator.validate(jsonSchema, data);
+ if (errors) {
+ this.throw(422, 'Validation Failed', {
+ code: 'invalid_param',
+ errors,
+ });
+ }
+ },
};
// app.js
'use strict';
const Parameter = require('parameter');
+ const Ajv = require('ajv');
module.exports = app => {
/**
* Validate
*
* ```js
* app.validator.addRule('jsonString', (rule, value) => {
* try {
* JSON.parse(value);
* } catch (err) {
* return 'must be json string';
* }
* });
*
* app.validator.validate({
* name: 'string',
* info: { type: 'jsonString', required: false },
* }, {
* name: 'Egg',
* info: '{"foo": "bar"}',
* });
* ```
*/
app.validator = new Parameter();
+ app.jsonSchemaValidator = new Ajv();
};
parameter 的 addRule 来扩展?这个思路是 @atian25 提出的,我想了一下,场景可能不太合适。
addRule 更多是针对整个 json 的某个字段的 rule 扩展,当然通过 addRule 它也可以实现对根的 json 进行全局的校验,但是这样使用起来会很怪
毕竟 jsonSchema 不是所有人都熟悉,而且 jsonSchema 的代码量比描述的 json 其实要长很多。这样特性使用起来可能成本有点高
parameter 就能解决了这么复杂的检验规则还不如写代码,还得测试这个规则是否被测试过。
新增 jsonSchemaValidate 接口没问题啊
放到 egg-validate 里面么?还是单独实现一个 egg-ajv 插件好了
@popomore 我现在就是写代码分步校验的,上面只是相对简单的情况,目前我遇到的场景比描述的复杂很多,json 结构非常复杂而且包含许多 存在这个字段,才会有另外某些字段 的操蛋逻辑
代码可能就会变成这样
const requestBody = ctx.request.body;
// 校验最外层结构
ctx.validate(rule, requestBody);
if (requestBody.xxx === 1) {
// 对应情况 1 的校验
ctx.validate(rule1, requestBody.xxxData);
} else if (requestBody.xxx === 2) {
// 对应情况 2 的校验
ctx.validate(rule2, requestBody.xxxData);
} else {
// 可能还会有第三层的分支逻辑...
}
这样就会变得非常蛋疼,虽然 jsonSchema 相对复杂,但它是标准的,通过 ajv 是能保证 jsonSchema 正确性的,从而也能通过正确的 jsonSchema 去校验真实的复杂数据源
相对写分支逻辑,虽然复杂但还是 jsonSchema 更优雅点
另外目前的 parameter 不支持对一个字段定义多个类型也是个痛点,而这是 jsonSchema 灵活的地方
{
"type": "object",
"properties": {
"name": {
"type": [
"string",
"number"
]
}
}
}
@dead-horse 我觉得可以放 egg-validate,因为本质还是进行输入校验,只是校验规则的定义不一样
我的提案是向下兼容的,不影响旧有 ctx.validate 的使用,只是多注入一个接口
接口名 validateJSON 吧。
独立一个 egg-validate-json 也行。
还有一种方案是 addRule(json) 加个 type,这样既可以用 scheme 单独验证某个字段,需要全部验证的时候就 ctx.validate(rule, { body: requestBody } )
@atian25 addRule 不考虑了,根本是两种不一样的校验规则定义,还是区分开好
名字建议是 validateBySchema,我觉得和 json 不搭,旧有的 validate 校验的也是 json
现在基本要明确的就是基于 egg-validate 增加接口还是独立一个插件
我建议独立插件好了,egg-validate-schema,API 是 validateBySchema
cc @eggjs/core 投票下吧,好让 @mansonchor 开工~
直接在本 comment 用 👍 👎 投?
@eggjs/core @atian25 只有两票...
如果都没意见我就开工咯,预计这周内抽时间完成
Most helpful comment
我建议独立插件好了,
egg-validate-schema,API 是validateBySchemacc @eggjs/core 投票下吧,好让 @mansonchor 开工~
直接在本 comment 用 👍 👎 投?