lin-mizar
Version:
The core library of Lin CMS
345 lines (344 loc) • 13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Rule = exports.LinValidator = void 0;
const tslib_1 = require("tslib");
const lodash_1 = require("lodash");
const exception_1 = require("../exception");
const validator_1 = tslib_1.__importDefault(require("validator"));
const extended_validator_1 = require("./extended-validator");
const utils_1 = require("../utils");
/**
* 强大的校验器
* 支持optional,支持array,支持nested object
*/
class LinValidator {
constructor() {
/**
* 装载数据的容器
*/
this.data = {};
/**
* 解析后的数据容器
*/
this.parsed = {};
/**
* 数据校验错误容器
*/
this.errors = [];
}
/**
* 校验
* @param ctx koa context
* @param alias 别名
*/
async validate(ctx, alias) {
this.alias = alias;
this.data = {
body: ctx.request.body,
query: ctx.request.query,
path: ctx.params,
header: ctx.request.header
};
const tmpData = lodash_1.cloneDeep(this.data);
this.parsed = Object.assign(Object.assign({}, tmpData), { default: {} });
if (!(await this.checkRules())) {
let obj = {};
if (this.errors.length === 1) {
obj = this.errors[0].message;
}
else {
for (const err of this.errors) {
obj[err.key] = err.message;
}
}
throw new exception_1.ParametersException({ message: obj });
}
else {
ctx.v = this;
return this;
}
}
replace(keys) {
// key 是原来的名字
if (!this.alias) {
return keys;
}
let arr = [];
for (let key of keys) {
if (this.alias[key]) {
this[this.alias[key]] = this[key];
lodash_1.unset(this, key);
arr.push(this.alias[key]);
}
else {
arr.push(key);
}
}
return arr;
}
isOptional(val) {
// undefined , null , "" , " ", 皆通过
if (val === void 0) {
return true;
}
if (val === null) {
return true;
}
if (typeof val === 'string') {
return val === '' || val.trim() === '';
}
return false;
}
async checkRules() {
// 筛选出是Rule或Rules的key
// 添加规则校验 validateKey
// default校验规则 throw
let keys = utils_1.getAllFieldNames(this, {
filter: key => {
const value = this[key];
if (lodash_1.isArray(value)) {
if (value.length === 0) {
return false;
}
for (const it of value) {
if (!(it instanceof Rule)) {
throw new Error('every item must be a instance of Rule');
}
}
return true;
}
else {
return value instanceof Rule;
}
}
});
// 此处进行别名替换
keys = this.replace(keys);
for (const key of keys) {
// 标志位
let stoppedFlag = false;
let optional = false;
const value = this[key];
let defaultVal;
// 如果没有传入这个参数,检查这个校验链中是否有 isOptional 如果有通过,否则抛异常
// 去data下找key,二级目录查找 dataKey 为一级目录的路径
const [dataKey, dataVal] = this.findInData(key);
if (this.isOptional(dataVal)) {
let message;
if (lodash_1.isArray(value)) {
for (const it of value) {
if (it.optional) {
defaultVal = it.defaultValue && it.defaultValue;
optional = true;
}
else {
if (!message) {
message = it.message;
}
}
}
}
else {
if (value.optional) {
defaultVal = value.defaultValue && value.defaultValue;
optional = true;
}
else {
message = value.message;
}
}
// 没有 isOptional 抛异常
// 有 isOptional 取它的默认值
if (!optional) {
this.errors.push({ key, message: message || `${key}不可为空` });
}
else {
this.parsed['default'][key] = defaultVal;
}
}
else {
if (lodash_1.isArray(value)) {
const errs = [];
for (const it of value) {
// 当rule的optional为false时,进行校验
if (!stoppedFlag && !it.optional) {
let valid;
valid = await it.validate(this.data[dataKey][key]);
if (!valid) {
errs.push(it.message);
// 如果当前key已有错误,则置stoppedFlag为true,后续会直接跳过校验
stoppedFlag = true;
}
}
if (it.parsedValue !== void 0) {
this.parsed[dataKey][key] = it.parsedValue;
}
}
if (errs.length !== 0) {
this.errors.push({ key, message: errs });
}
}
else {
const errs = [];
if (!stoppedFlag && !value.optional) {
let valid;
valid = await value.validate(this.data[dataKey][key]);
if (!valid) {
errs.push(value.message);
// 如果当前key已有错误,则置stoppedFlag为true,后续会直接跳过校验
stoppedFlag = true;
}
}
if (value.parsedValue !== void 0) {
this.parsed[dataKey][key] = value.parsedValue;
}
if (errs.length !== 0) {
this.errors.push({ key, message: errs });
}
}
}
}
let validateFuncKeys = utils_1.getAllMethodNames(this, {
filter: key => /validate([A-Z])\w+/g.test(key) && typeof this[key] === 'function'
});
for (const validateFuncKey of validateFuncKeys) {
// 最后校验规则函数
const customerValidateFunc = lodash_1.get(this, validateFuncKey);
// 规则函数,每个都try,catch,并将错误信息加入到整体错误信息中
// 第一个参数为data
// 自定义校验函数,第一个参数是校验是否成功,第二个参数为错误信息
let validRes;
try {
validRes = await customerValidateFunc.call(this, this.data);
if (lodash_1.isArray(validRes) && !validRes[0]) {
let key;
if (validRes[2]) {
key = validRes[2];
}
else {
key = this.getValidateFuncKey(validateFuncKey);
}
this.errors.push({ key, message: validRes[1] });
}
else if (!validRes) {
let key = this.getValidateFuncKey(validateFuncKey);
// 如果自定函数没有给出错误信息,那么错误信息为默认
this.errors.push({ key, message: '参数错误' });
}
}
catch (error) {
const key = this.getValidateFuncKey(validateFuncKey);
if (error instanceof exception_1.HttpException) {
this.errors.push({ key, message: error.message });
}
else {
this.errors.push({ key, message: error.message });
}
}
}
return this.errors.length === 0;
}
/**
* 获得规则函数的key
* @param validateFuncKey 规则函数的名称
*/
getValidateFuncKey(validateFuncKey) {
return validateFuncKey.replace('validate', '');
}
/**
* 取参数里的值;如果参数不能被解析,则返回没有被解析的值
* @param path 参数所在的路径,如 a.b
* @param parsed 是否取已经解析后的数据,默认为true
* @param defaultVal 默认值,当路径指向的值不存在,取默认值
*/
get(path, parsed = true) {
let defaultVal;
if (arguments.length >= 3) {
defaultVal = arguments[2];
}
if (parsed) {
const key = lodash_1.get(this.parsed, path, defaultVal && defaultVal);
if (!this.isOptional(key)) {
return key;
}
else {
const index = path.lastIndexOf('.');
const suffix = path.substring(index + 1, path.length);
return lodash_1.get(this.parsed['default'], suffix, defaultVal && defaultVal);
}
}
else {
return lodash_1.get(this.data, path, defaultVal && defaultVal);
}
}
findInData(key) {
const keys = Object.keys(this.data);
for (const k of keys) {
const val = lodash_1.get(this.data[k], key);
if (val !== void 0) {
return [k, val];
}
}
return [];
}
}
exports.LinValidator = LinValidator;
/**
* 规则类
*/
class Rule {
constructor(validateFunction, message, ...options) {
this.optional = false;
this.validateFunction = validateFunction;
this.message = message || '参数错误';
this.options = options;
if (this.validateFunction === 'isOptional') {
// 如果当前项为optional,检查该项是否存在,若不存在,将optional置为true
// 如果是optional,那么没有传入的参数,可以使用默认值
this.optional = true;
this.defaultValue = options && options[0];
}
}
validate(value) {
this.rawValue = value;
if (typeof this.validateFunction === 'function') {
return this.validateFunction(value, ...this.options);
}
else {
switch (this.validateFunction) {
case 'isInt':
if (typeof value === 'string') {
this.parsedValue = validator_1.default.toInt(value);
return validator_1.default.isInt(value, ...this.options);
}
else {
this.parsedValue = value;
return validator_1.default.isInt(String(value), ...this.options);
}
case 'isFloat':
if (typeof value === 'string') {
this.parsedValue = validator_1.default.toFloat(value);
return validator_1.default.isFloat(value, ...this.options);
}
else {
this.parsedValue = value;
return validator_1.default.isFloat(String(value), ...this.options);
}
case 'isBoolean':
if (typeof value === 'string') {
this.parsedValue = validator_1.default.toBoolean(value);
return validator_1.default.isBoolean(value);
}
else {
this.parsedValue = value;
return validator_1.default.isBoolean(String(value));
}
case 'isNotEmpty':
return extended_validator_1.extendedValidator.isNotEmpty(value);
default:
return validator_1.default[this.validateFunction](value, ...this.options);
}
}
}
}
exports.Rule = Rule;