The validater based on ajv for egg.js.
Egg-ajv consider each schema as a small piece, which compose a complete schema by using the key words - $ref, $merge and $patch - to reduce the code repetition.
$ npm i egg-ajv --save
Change ${app_root}/config/plugin.js
to enable egg-ajv plugin:
exports.ajv = {
enable: true,
package: 'egg-ajv',
};
Configure ajv information in ${app_root}/config/config.default.js
:
config.ajv = {
keyword: 'schema', // to indicate the namespace and path of schemas, default as 'schema'
allErrors: true, // required for custom error message
jsonPointers: true, // required for custom error message
}
And all the options of Ajv is supported.
/**
* json validation
*
* @param {string|object} schema - string for schema id and object for Ajv rules
* @param {object} value - default as ctx.request.body
* @return {undefine} throw an exception instead
*/
async validate(schema, value) {}
We need to put our schemas at the /app/${config.ajv.keyword}
directory, which would be automaticlly loaded into the instance of Ajv accessed by app.ajv
. To make it simple, we persumed that the keyword is 'schema'.
Each file is designated an id based on it's path, such as the id of app/schema/a/b.js
is schema.a.b
// app/schema/definition.js
module.exports = {
int: { type: 'integer' },
str: { type: 'string' },
};
// app/schema/user.js
module.exports = {
properties: {
id: {
$ref: 'schema.definition#/int',
},
name: {
type: 'string',
},
password: {
type: 'string',
},
},
required: [ 'name', 'password', 'id' ],
$async: true,
additionalProperties: false,
};
// app/controller/user.js
exports.create = async ctx => {
await this.ctx.validate('schema.pagination', this.ctx.request.body);
};
// app/schema/user.js
module.exports = {
properties: {
name: {
type: 'string',
},
password: {
type: 'string',
},
},
required: [ 'name', 'password' ],
$async: true,
additionalProperties: false,
};
// app/controller/user.js
const rule = {
$async: true,
$merge: {
source: {
properties: {
user: { $ref: 'schema.user#' },
},
},
with: {
properties: {
age: {
type: 'number',
},
},
// array would be overrided instead of merged
required: [ 'password', 'name', 'age' ],
},
},
};
exports.create = async ctx => {
await this.ctx.validate(rule, this.ctx.request.body);
};
{
"type": "object",
"properties": {
"size": {
"type": "number",
"minimum": 4
}
},
"errorMessage": {
"properties": {
"size": "size should be a number bigger or equal to 4, current value is ${/size}"
}
}
}
check detail at ajv-errors
An exception will be thrown when a validation failed, so we'd better catch it at the middleware, but Egg-ajv doesn't do it for the sake of expansibility.
Try something like this:
// app/middleware/error.js
const { ValidationError } = require('ajv');
module.exports = () => function* (next) {
try {
yield next;
} catch (e) {
if (e instanceof ValidationError) {
this.body = {
code: 422,
msg: '请求参数错误',
errors: e.errors,
};
this.status = 422;
} else {
throw e;
}
}
};
For more information, check the test app example