0%

Koa.js 自定义POST请求参数验证中间件

  1. 用例
  2. 源码

用例

用于验证POST或GET请求参数是否满足要求,用例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const validate = require('validate.js');
const Koa = require('Koa');
const app = new Koa();

// 数组形式
app.use(validate(['account', 'password']));

// 键值对形式:值为 Boolean
app.use(validate({account: true, password: true}));

// 键值对形式:在值中指定类型、是否必要、取值等
app.use(validate({
account: {required: true, type: 'string'},
password: {required: true, type: 'string'},
num: {required: true, type: 'number', values: [0,1,2]}
}));

app.listen(3000);

源码

validate.js

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
const logger = require('../../utils/logger').logger;
const chalk = require('chalk');

if (!String.prototype.t) {
String.prototype.t = function(...args) {
if (args.length === 0) {
return this.toString();
}

const template = '%t';
let str = this.toString();

for (let i = 0; i < args.length; i++) {
let idx = str.indexOf(template);
if (idx === -1) break;
if (typeof args[i] === 'object') {
args[i] = JSON.stringify(args[i]);
}
str = str.substr(0, idx) + args[i] +
str.substring(idx + template.length, str.length);
}
return str;
}
}

/**
* 参数验证中间件:针对post验证body,针对get验证query
* @param {string[]|{}} rules 验证规则
* @example
* validate(['account', 'password'])
* validate({account: true, password: true})
* validate({
* account: {required: true, type: 'string'},
* password: {required: true, type: 'string'},
* num: {required: true, type: 'number', values: [0,1,2]}
* })
* @returns {Function} Koa中间件函数
*/
const validate = (rules) => {
let m_logger = console;
if (logger && logger.isInitiatial()) {
m_logger = logger;
}
return async(ctx, next) => {
const method = ctx.request.method;

if (method !== 'POST' && method !== 'GET') {
console.error(`Warning: Cannot validate except from POST and GET method`);
return next();
}

const obj = method === 'POST' ?
ctx.request.body :
ctx.request.query;

if (!obj) {
if (method === 'POST') throw new Error('POST body is null');
else if (method === 'GET') throw new Error('Query parameter is null');
else throw new Error('Object is null');
}

if (!rules)
return next();


/**
* 定义异常处理函数
*/

const ParaMissingFunc = (key, obj) => {
const errMsg = 'Missing [%t] Parameter: [%t]';
return ctx.throw(403, errMsg.t(chalk.yellow(method), chalk.cyan(key)));
};
const ParaTypeErrFunc = (key, obj, ruleType) => {
const errMsg = 'Type error of Parameter: [%t] ( require [%t] )';
return ctx.throw(403, errMsg.t(chalk.yellow(key), chalk.cyan(ruleType)));
};
const InvalidParaValueErrFunc = (key, obj) => {
const errMsg = 'Invalid parameter value: %t';
return ctx.throw(403, errMsg.t(chalk.cyan(obj[key])));
}

if (Array.isArray(rules)) {
for (let key of rules) {
if (obj[key] === undefined) {
return ParaMissingFunc(key, obj);
}
}
return next();
} else if (typeof rules === 'object') {
for (let key in rules) {
const rule = rules[key];
if (typeof rule === 'boolean') {
if (rule === true && obj[key] === undefined) {
return ParaMissingFunc(key, obj);
}
} else if (typeof rule === 'object') {
if (rule.required !== undefined && rule.required === true) {
if (obj[key] === undefined) {
return ParaMissingFunc(key, obj);
}
}

if (rule.type !== undefined) {
if (typeof obj[key] !== rule.type) {
return ParaTypeErrFunc(key, obj, rule.type);
}
}

if (Array.isArray(rule.values)) {
if (rule.values.indexOf(obj[key]) === -1) {
return InvalidParaValueErrFunc(key, obj);
}
}
} else {
throw new Error(`Unexpected rule type: ${typeof rule} - ( key [${key}] in ${JSON.stringify(rules)} )`);
}
}
await next();
} else {
throw new Error('Invalid rules type: ' + typeof rules);
}
}
};

module.exports = validate;
  • logger是日志中间件,可替换为console
  • chalk用于给控制台输出上色
  • String.prototype.t用于格式化字符串,如:
1
const str = '%t, %t'.t(1, 2);   // str == '1, 2'