不管以什么语言编写程序,我们都会写大量的代码用来做参数验证以及错误处理。Node.js 更甚,js 的单线程特性意味着一个简单的语法错误都会导致整个程序崩溃。毫不客气的说,在编写 Node.js 应用时,有三分之一的代码都是在验证参数以及处理错误,而这些代码通常与业务逻辑代码杂糅在一起,随着应用越写越大,代码变得臃肿难以维护。中间件的设计哲学则让参数验证以及错误处理变得极为简单与优雅,我们只需将相关代码编写成中间件,并在路由之前加载,每个请求到来时都会找到对应的规则,如果验证失败直接返回错误,验证成功后才会将请求传递到下一个中间件,这样就实现了验证逻辑与业务逻辑的分离。koa-scheme 是 koa 的一个参数验证中间件,同时它还可以对请求做一些过滤和格式化的工作。我们来看个例子:
app.js
var koa = require('koa');
var bodyParser = require('koa-bodyparser');
var scheme = require('koa-scheme');
var app = koa();
app.use(bodyParser());
app.use(scheme(__dirname + '/scheme', {debug: true}));
app.use(function* () {
if (this.path === '/signup') {
console.log(this.request.body.password);
this.body = {
user: {
id: this.request.body.password
}
};
}
});
app.listen(3000);
scheme.js
var validator = require('validator');
var crypto = require('crypto');
module.exports = {
"/(.*)": {
"request": {
"header.version": /^[0-9]+$/
}
},
"(POST|put) /signup": {
"request": {
"body": {
"name": /^[a-zA-Z]+$/,
"age": validator.isNumeric,
"email": validator.isEmail,
"gender": /^(male|female)$/,
"password": /^[a-zA-Z0-9]{6,12}$/,
"repassword": checkRepassword
}
},
"response": {
"body.user.id": /^[a-zA-Z0-9]{32}$/
}
}
};
function md5 (str) {
return crypto.createHash('md5').update(str).digest('hex');
}
function checkRepassword(repassword) {
var body = this.request.body;
if (repassword !== body.password) {
return this.throw(400, '两次密码不一致!');
}
body.password = md5(body.password);
return true;
}
建议读者亲自运行以上代码调试一下。从 sheme.js 中可以看出,我们对所有接收的请求都会检查头中的 version 字段,假如没有该字段或该字段不是数字类型都会返回一个错误。对于 post 或 put 到 /signup 路径的请求,既会检查头中的 version 字段,又会分别检查请求体和响应体中的字段。需要说明的两点:我们在 checkRepassword 中先检查了两次输入的密码是否一致,若不一致则返回错误,若一致则将密码 md5 加密,这样我们在 app.js 中打印的就是加密后的密码了;假如我们只想验证嵌套比较深的某个字段而不是所有的字段时,只需像 body.user.id
那样使用 .
拼接即可。
我们可以把所有的参数验证以及格式化的工作都放到 scheme.js 中,如果需要验证的路径及规则过多的话,则可以按路径拆分成不同的小文件引入到 scheme.js,我们唯一需要保证的就是后续的中间件收到的数据格式都是正确的,这样我们就不必在业务逻辑中编写大量重复的、繁琐的参数验证代码了。