- 在core文件下添加utils.js和lin-validator.js文件
執行npm install lin-mizar --save安裝校驗依賴
這兩個文件必須依賴http-exception.js文件
utils.js
const jwt = require('jsonwebtoken')
/***
*
*/
const findMembers = function (instance, {
prefix,
specifiedType,
filter
}) {
// 遞歸函數
function _find(instance) {
//基線條件(跳出遞歸)
if (instance.__proto__ === null)
return []
let names = Reflect.ownKeys(instance)
names = names.filter((name) => {
// 過濾掉不滿足條件的屬性或方法名
return _shouldKeep(name)
})
return [...names, ..._find(instance.__proto__)]
}
function _shouldKeep(value) {
if (filter) {
if (filter(value)) {
return true
}
}
if (prefix)
if (value.startsWith(prefix))
return true
if (specifiedType)
if (instance[value] instanceof specifiedType)
return true
}
return _find(instance)
}
const generateToken = function(uid, scope){
const secretKey = global.config.security.secretKey
const expiresIn = global.config.security.expiresIn
const token = jwt.sign({
uid,
scope
},secretKey,{
expiresIn
})
return token
}
module.exports = {
findMembers,
generateToken,
}
lin-validator.js
/**
* Lin-Validator v2
* 作者:7七月
* 微信公衆號:林間有風
*/
const validator = require('validator')
const {
ParameterException
} = require('./http-exception')
const {
get,
last,
set,
cloneDeep
} = require("lodash")
const {
findMembers
} = require('./util')
class LinValidator {
constructor() {
this.data = {}
this.parsed = {}
}
_assembleAllParams(ctx) {
return {
body: ctx.request.body,
query: ctx.request.query,
path: ctx.params,
header: ctx.request.header
}
}
get(path, parsed = true) {
if (parsed) {
const value = get(this.parsed, path, null)
if (value == null) {
const keys = path.split('.')
const key = last(keys)
return get(this.parsed.default, key)
}
return value
} else {
return get(this.data, path)
}
}
_findMembersFilter(key) {
if (/validate([A-Z])\w+/g.test(key)) {
return true
}
if (this[key] instanceof Array) {
this[key].forEach(value => {
const isRuleType = value instanceof Rule
if (!isRuleType) {
throw new Error('驗證數組必須全部爲Rule類型')
}
})
return true
}
return false
}
async validate(ctx, alias = {}) {
this.alias = alias
let params = this._assembleAllParams(ctx)
this.data = cloneDeep(params)
this.parsed = cloneDeep(params)
const memberKeys = findMembers(this, {
filter: this._findMembersFilter.bind(this)
})
const errorMsgs = []
// const map = new Map(memberKeys)
for (let key of memberKeys) {
const result = await this._check(key, alias)
if (!result.success) {
errorMsgs.push(result.msg)
}
}
if (errorMsgs.length != 0) {
throw new ParameterException(errorMsgs)
}
ctx.v = this
return this
}
async _check(key, alias = {}) {
const isCustomFunc = typeof (this[key]) == 'function' ? true : false
let result;
if (isCustomFunc) {
try {
await this[key](this.data)
result = new RuleResult(true)
} catch (error) {
result = new RuleResult(false, error.msg || error.message || '參數錯誤')
}
// 函數驗證
} else {
// 屬性驗證, 數組,內有一組Rule
const rules = this[key]
const ruleField = new RuleField(rules)
// 別名替換
key = alias[key] ? alias[key] : key
const param = this._findParam(key)
result = ruleField.validate(param.value)
if (result.pass) {
// 如果參數路徑不存在,往往是因爲用戶傳了空值,而又設置了默認值
if (param.path.length == 0) {
set(this.parsed, ['default', key], result.legalValue)
} else {
set(this.parsed, param.path, result.legalValue)
}
}
}
if (!result.pass) {
const msg = `${isCustomFunc ? '' : key}${result.msg}`
return {
msg: msg,
success: false
}
}
return {
msg: 'ok',
success: true
}
}
_findParam(key) {
let value
value = get(this.data, ['query', key])
if (value) {
return {
value,
path: ['query', key]
}
}
value = get(this.data, ['body', key])
if (value) {
return {
value,
path: ['body', key]
}
}
value = get(this.data, ['path', key])
if (value) {
return {
value,
path: ['path', key]
}
}
value = get(this.data, ['header', key])
if (value) {
return {
value,
path: ['header', key]
}
}
return {
value: null,
path: []
}
}
}
class RuleResult {
constructor(pass, msg = '') {
Object.assign(this, {
pass,
msg
})
}
}
class RuleFieldResult extends RuleResult {
constructor(pass, msg = '', legalValue = null) {
super(pass, msg)
this.legalValue = legalValue
}
}
class Rule {
constructor(name, msg, ...params) {
Object.assign(this, {
name,
msg,
params
})
}
validate(field) {
if (this.name == 'isOptional')
return new RuleResult(true)
if (!validator[this.name](field + '', ...this.params)) {
return new RuleResult(false, this.msg || this.message || '參數錯誤')
}
return new RuleResult(true, '')
}
}
class RuleField {
constructor(rules) {
this.rules = rules
}
validate(field) {
if (field == null) {
// 如果字段爲空
const allowEmpty = this._allowEmpty()
const defaultValue = this._hasDefault()
if (allowEmpty) {
return new RuleFieldResult(true, '', defaultValue)
} else {
return new RuleFieldResult(false, '字段是必填參數')
}
}
const filedResult = new RuleFieldResult(false)
for (let rule of this.rules) {
let result = rule.validate(field)
if (!result.pass) {
filedResult.msg = result.msg
filedResult.legalValue = null
// 一旦一條校驗規則不通過,則立即終止這個字段的驗證
return filedResult
}
}
return new RuleFieldResult(true, '', this._convert(field))
}
_convert(value) {
for (let rule of this.rules) {
if (rule.name == 'isInt') {
return parseInt(value)
}
if (rule.name == 'isFloat') {
return parseFloat(value)
}
if (rule.name == 'isBoolean') {
return value ? true : false
}
}
return value
}
_allowEmpty() {
for (let rule of this.rules) {
if (rule.name == 'isOptional') {
return true
}
}
return false
}
_hasDefault() {
for (let rule of this.rules) {
const defaultValue = rule.params[0]
if (rule.name == 'isOptional') {
return defaultValue
}
}
}
}
module.exports = {
Rule,
LinValidator
}
- 在app文件夾下創建validators文件夾,並創建validator.js文件
const { LinValidator, Rule } = require('../../core/lin-validator')
// 校驗參數是否爲正整數
class ValidationInteger extends LinValidator{
constructor() {
super()
this.id = [
// 這裏可以添加多個校驗規則,但是規則是且的關係
// 三個參數:第一個參數:需要滿足的規則,第二個參數:提示信息,第三個參數:可選參數
new Rule('isInt', '參數必須爲正整數', {min:1})
// new Rule ('isNotEmpty', '必須傳入參數')
]
}
}
module.exports = { ValidationInteger }
- 在clssic.js文件中引入校驗規則並使用
const Router = require('koa-router')
const router = new Router()
const { ValidationInteger } = require('../../validators/validator')
router.post('/v1/:id/classic/latest',async (ctx, next)=>{
const param = ctx.params
const query = ctx.request.query
const header = ctx.request.header
const body = ctx.request.body
const V = await new ValidationInteger().validate(ctx)
})
module.exports = router
- 在postman中進行測試