使用Lin-Validator進行參數校驗

Lin-Validator參考文檔

  1. 在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
}
  1. 在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 }
  1. 在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
  1. 在postman中進行測試
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章