preface

Continue my self-built blog journey, Node build blog background, using KOA + mysql to achieve. This time do the validation of the request parameters 👉

Project source: github.com/Ray-daydayu…

The cause of

I have been using yAPI to simulate the interface, which provides the simulation of interface verification. Every time I request the verification parameter, I will be reminded that the request parameter type is wrong. When I wrote the API myself, I got a little confused. How does the background work? (at this time I use Java classmate smiled smile, you see, I write a type good 😜. … Keep smiling 😊)

Then I started searching for koA-related plugins. There were a lot of them, such as KOA-Parameter, KOA2-Validator, and koA-middle-Validator. Then I in line with my learning psychology, write a simple parameter verification (in fact, the level is limited, do not know how to combine with what I have, want to use my own encapsulation of the exception processing 🙃).

When it comes to parameter validation, one guy who seems to be famous is Joi, who uses it something like this (source code) 👇

var Joi = require('joi');
var schema = Joi.object({
    username: Joi.string().min(3).max(30).required(),
    isA: Joi.boolean(),
    AVal: Joi.number(),
    isB: Joi.boolean(),
    BVal: Joi.string()
})
.with('isA'.'AVal')
.with('isB'.'BVal')
.without('isA'.'isB')
.or('isA'.'isB');

var input = {
	username: 'zzbo'
}

var output = Joi.validate(input, schema);
//error: ValidationError: "value" must contain at least one of [isA, isB]
Copy the code

At the time, I was surprised to see it written like this. As a kid, I didn’t know anything about object orientation and chain calls, and it really hit me. Could I try to implement something similar? Once this idea was born, it could not be put out.

Principle and code implementation

Usage examples

First of all, the first use of finished products, the level is limited, only to achieve the sample 👇

// Define verification rules
let schema = {
    page: new ParamCheck().isNumber().min(1).isRequired(),
    pagesize: new ParamCheck().isNumber().min(1).isRequired(),
    moreDetail: new ParamCheck().isBoolean().isRequired(),
    tagId: new ParamCheck().isNumber().min(1),
    categoryId: new ParamCheck().isNumber().min(1)}// Wait for the verification result, and throw an exception if it fails (combined with my previous exception handling)
await ParamCheck.check(params, schema, ctx)
Copy the code

I only considered validation of the passed object, and although new looked ugly, it fulfilled my requirements for the time being.

Thought analysis

The whole idea is to define a parameter verification class, and call the corresponding method to collect verification rules (I defined as the attribute rules of the instance) after initializing the instance. By defining a rule object, loop out each rule definition when calling class static methods, value it by key, and then check it against the rule set. There are two main tasks:

  • Collection of rules
  • Validation of validation rules

Collection of rules

I started by defining three types of rules according to my requirements: type rules, length rules, and must-have rules. Then there is the code implementation 👇

class paramCheck {
	constructor() {
		this.rules = {} // rule set type: indicates the type; Min and Max represent the upper and lower limits of length. IsRequired indicates whether it isRequired
	}
	// Validate static methods
	static async check(params, schema, ctx) {
		const keys = Object.keys(schema)
		try {
		    // Loop out the rule set for verification
			for (let i = 0; i < keys.length; i++) {
				const key = keys[i]
				let rules = schema[key].rules // Get the rule set
				rulesCheck(params, key, rules)// Call function verification
			}
			return
		} catch (error) {
		    // Catch thrown errors
			switch (error.name) {
				case "paramNotComplete":
					await PARAM_NOT_COMPLETE(ctx, error.message) // The exception handler defined earlier
					break
				case "paramTypeError":
					await PARAM_TYPE_ERROR(ctx, error.message)
					break
				case "paramNotValid":
					await PARAM_NOT_VALID(ctx, error.message)
					break
				default:
					await FAIL(ctx, error.message)
					break}}}// Type rules
	isString() {
		this.rules.type = "string" // Write type rules
		return this // Return this chain call
	}
	isNumber() {
		this.rules.type = "number"
		return this
	}
	isBoolean() {
		this.rules.type = "boolean"
		return this
	}
	isObject() {
		this.rules.type = Object // Object and array are treated specially
		return this
	}
	isArray() {
		this.rules.type = Array
		return this
	}
	// Length rule
	min(min) {
		this.rules.min = min // Write the length rule
		return this
	}
	max(max) {
		this.rules.max = max
		return this
	}
	// Whether it is necessary
	isRequired() {
		this.rules.isRequired = true 
		return this}}Copy the code

Validation of rules

Rules are validated in a certain order, whether must be > type rule > length rule, so must be validated in order. The code looks like this

function rulesCheck(params, key, rules) {
	if(! requiredCheck()) {return false
	}
	typeCheck()
	lengthCheck()
	return true
	function requiredCheck() {... }function typeCheck() {... }function lengthCheck() {... }}// Argument error class
class ParamError extends Error {
	constructor(msg, name) {
		super(msg)
		this.name = name
	}
}

Copy the code
  1. Whether must
function requiredCheck() {
    // If the parameter is required, but no, an error is reported
    if(rules.isRequired && ! params.hasOwnProperty(key)) {throw new ParamError('Request parameters${key}Missing `."paramNotComplete")}if(! rules.isRequired && ! params.hasOwnProperty(key)) {// The parameter is not required, but no further validation is required
        return false
    }
    // All other cases need to be checked
    return true
}
Copy the code
  1. Type checking
function typeCheck() {
    if (rules.type) {
	    const type = rules.type
	    // Check type
		if (
			typeof params[key] === type ||
            (typeoftype ! = ="string" && params[key] instanceof type)
        ) {
		    return
        }
        // Throw an error
        throw new ParamError('Request parameters${key}Type error '."paramTypeError")}}Copy the code
  1. Type checking
function lengthCheck() {
	const min = rules.min ? rules.min : 0
	const max = rules.max ? rules.max : Infinity
	const type = rules.type
	let length = 0
	// Check the length of arrays and strings
	if (type === "string" || type === Array) {
		length = params[key].length
	}
	// Check the size of the number
	if (type === "number") {
		length = params[key]
	}
	if (length < min || length > max) {
		throw new ParamError('Request parameters${key}Length invalid '."paramNotValid")}}Copy the code

conclusion

A simple validation of the parameters has been done, which is rudimentary, but satisfies my needs. A novice, the code to write insufficient place, please forgive me 🙏