首页 > 其他分享 >koa中使用joi进行参数校验

koa中使用joi进行参数校验

时间:2024-08-12 11:40:53浏览次数:6  
标签:const string koa Joi 校验 error joi schema

koa中使用joi进行参数校验

超人鸭IP属地: 广东 0.312021.07.06 18:23:59字数 1,395阅读 6,498

在编写api的时候通常都需要对参数进行校验,包括参数的类型、必填等;如果是字符串,是否可以为空、该符合什么规则等;如果是数字,最大值最小值是什么等等等等。
在koa中我推荐使用 joi 这个库来进行参数校验。joi文档

安装: npm install joi --save
引入: import Joi from 'joi'

下面来看看joi的用法:

基础使用:

使用joi进行校验,首先要定义它的校验规则,也叫schema

const schema = Joi.string()

上面就定义了一个校验字符串类型的规则,这个schema会有一个 validate方法,传入需要校验的值:

const result = schema.validate('1')
console.log(result)
// 此时result为 { value: '1' }

validate方法会返回一个对象,如果验证通过,就只会返回value属性,如果验证错误,就还有一个error对象,其中error对象的message描述了失败原因:

const schema = Joi.string()
const result = schema.validate(1)
console.log(result)
// result:
{
  value: 1,
  error: [Error [ValidationError]: "value" must be a string] {
    _original: 1,
    details: [ [Object] ]
  }
}

console.log(result.error.message)
// "value" must be a string

验证对象

既然是对koa的接口进行参数校验,无论是ctx.request.query还是ctx.request.body都是对象,那么使用joi校验基本都是对象校验了。

与上面验证string一样,都是定义一个schema:

const schema = Joi.object({
  name: Joi.string().allow('').required(),
  age: Joi.number().min(18).max(35),
  skill: Joi.array().items(Joi.string()).length(3)
})

const { error } = schema.validate({
  name: 'chaorenya',
  age: 20,
  skill: ['vue', 'koa', 'react']
})
console.log(error) // undefined

其实就是在object中组合嵌套其他规则,顺便解释下上面几个规则:

name: Joi.string().allow('').required()
代表着name属性为必填,而且可以支持空字符串,Joi.string()默认情况不支持空字符串。

age: Joi.number().min(18).max(35)
代表age属性需要是一个数字,且最小为18最大为35

skill: Joi.array().items(Joi.string()).length(3)
代表skill属性为一个数组且数组长度为3且都得是字符串

其中age和skill没有设置required,可以不填,但是填了的话就必须按照上面的规则。
而且这个对象不能存在其他属性,如果需要允许其他属性的出现,需要在跟上一个unknown方法:

const { error } = schema.validate({
  name: 'chaorenya',
  age: 20,
  skill: ['vue', 'koa', 'react'],
  other: 'other'
}).unknown() // 允许出现其他字段

多个属性校验结合

有些情况某个字段的校验规则是根据另一个字段来规定的。

in:

const schema = Joi.object({
  a: Joi.array().items(Joi.number().valid(18,35)),
  b: Joi.number().valid(Joi.in('a'))
})
const { error } = schema.validate({
  a: [18, 35, 35],
  b: 19
})
console.log(error.message) // "b" must be [ref:a]

a属性表示是个数组,且为number类型,并且只允许18、35这两个值当中二选一,也就是上面的valid方法。
b属性表示必须为a属性其中的一项。

ref:

const schema = Joi.object({
  a: Joi.string().pattern(/^[1-9][0-9]*$/).required(),
  b: Joi.object({
    c: Joi.any().required(),
    d: Joi.ref('c'),
    e: Joi.ref('c', { ancestor: 1 }),
    f: Joi.ref('a', { ancestor: 2 })
  })
})
const { error } = schema.validate({
  a: '10',
  b: {
    c: 10,
    d: 10,
    e: 10,
    f: '10'
  }
})
console.log(error) // undefined

使用ref可以指向其他属性,需要填写上属性名,然后可以指定在哪一层对象中寻找指向的属性,也就是上面的{ ancestor: 1 },不写的情况也就是上面例子中的b.c,会在当前对象中寻找,等同于写上{ ancestor: 1 }{ ancestor: 2 }代表在上一层对象中寻找,如果找不到就会验证失败。
所以上面的例子就是a属性为/^[1-9][0-9]*$/正则校验通过的字符串,b为对象,b.c可以为任何类型,b.d要求与b.c一致,b.e也要求与b.c一致,b.f要求与外面的a属性一致。

with、without、xor:

with:
const schema = Joi.object({
  a: Joi.any(),
  b: Joi.any()
}).with('a', 'b');
const { error } = schema.validate({
  a: 1
})
console.log(error.message) // "a" missing required peer "b"

使用with表示当设置的属性有一个出现了,其他也必须出现,上面的例子设置了a、b属性,需要同时存在或者同时不存在。

without:
const schema = Joi.object({
  a: Joi.any(),
  b: Joi.any()
}).without('a', ['b']);
const { error } = schema.validate({
  a: 1,
  b: 1
})
console.log(error.message)  // "a" conflict with forbidden peer "b"

without第一个参数设置条件字段,第二个参数为字段数组,表示第一个参数字段存在的话,第二个参数数组里面的字段都不能存在。上面的例子就是当a字段出现时,b字段就不能存在。

xor:
const schema = Joi.object({
  a: Joi.any(),
  b: Joi.any()
}).xor('a', 'b');
const { error } = schema.validate({
})
console.log(error.message) // "value" must contain at least one of [a, b]

xor表示设置的参数需要任何一个或多个存在。

when:

const schema = Joi.object({
  mode: Joi.string().allow('email', 'phone').required(),
  address: Joi.string().when('mode', { is: 'email', then: Joi.string().email() }).required()
});
const { error } = schema.validate({
  mode: 'email',
  address: '11111'
})
console.log(error.message)  // "address" must be a valid email

const { error } = schema.validate({
  mode: 'phone',
  address: '11111'
})
console.log(error) // undefined

when相当于条件判断,第一个参数传递属性名,is相当于ifthen后面就是is为真的时候的校验条件。
所以上面的例子就是mode字段只允许传入'email'和'phone',当mode字段为'email'的时候,address字段就会进行email校验(joi自带的字符串邮箱校验)。

封装中间件

先看看文件结构:


  image.png

既然是接口校验,那么校验的文件跟路由文件对应,每个中间件单独一个文件。

先看validator下的user文件:

import Joi from 'joi'

export const addUserSchema = Joi.object({
  userName: Joi.string().alphanum().required(),
  password: Joi.string().pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[^]{8,16}$/).required(),
  age: Joi.number().min(18).max(35).required(),
  createTime: Joi.date().timestamp('javascript').max('now').required()
})

定义了一个添加用户的接口参数校验,其中的createTime代表着毫秒时间戳,不能超过当前时间。

接下来看一下middlewares下的validateParams中间件:

/**
 * @description 参数校验中间件
 */

 import { RouterCtx, middleNext } from '../utils/types'
 import Joi from 'joi'
 import { ErrorModel } from '../utils/ResModel'
 import { paramsErrorInfo } from '../utils/ErrorInfo'
 
 function genValidateParams (method: string, schema: Joi.Schema) {
   async function validateParams (ctx: RouterCtx, next: middleNext) {
     let data: any
     if (method === 'get') {
       data = ctx.request.query
     } else {
       data = ctx.request.body
     }
     const { error } = schema.validate(data)
     if (error) {
       ctx.body = new ErrorModel({ // 返回错误信息,这里为其他地方封装,不用理会
         ...paramsErrorInfo,
         message: error.message || paramsErrorInfo.message
       })
       return
     }
     await next()
   }
   return validateParams
 }
 
 export default genValidateParams

因为koa的中间件参数固定为ctx与next,所以这里设计成一个工厂模式,可以将具体的schema传递进来。约定get方法参数传递在ctx.request.query,其他方法参数传递在ctx.request.body,对参数对象进行校验。

最后看一下路由:

import Router from 'koa-router'
const router = new Router({
  prefix: '/user'
})

import validateParams  from '../middlewares/validateParams'
import { addUserSchema } from '../validator/user'

router.post('/add', validateParams('post', addUserSchema), async (ctx, next) => {
  ctx.body = ctx.request.body
})

router.allowedMethods()

export default router

在进入主逻辑之前先走一遍参数校验,传递校验规则schema与method。
下面用postman模拟一下这个接口请求:

校验成功:


  image.png

校验失败:


  image.png

用joi这个插件来做参数校验还是非常nice的,超人鸭之前使用过ajv,但是文档比较难看懂,所以这次尝试了joi。

我是鸭子,祝你幸福。

标签:const,string,koa,Joi,校验,error,joi,schema
From: https://www.cnblogs.com/sexintercourse/p/18354644

相关文章

  • 从零开发ts装饰器管理koa路由
    从零开发ts装饰器管理koa路由超人鸭关注IP属地:广东0.3252022.05.3118:18:44字数4,882阅读1,518前言两年前刚学ts,当时搭了个简单的koa的demo,介绍了如何用装饰器管理koa的路由:TS装饰器初体验,用装饰器管理koa接口但是当时还只是demo学习,并没有真正在公司的项目中使用......
  • 025.Vue3入门,父页面给子页面传递数据,校验Props给出默认值
    1、App.vue代码:<template><Father/></template><scriptsetup>importFatherfrom'./view/Father.vue'</script><style></style>2、Father.vue代码<template><h3>父页面</h3><Chil......
  • 通信编码揭秘:(二)信道编码(汉明码、循环冗余校验码、里德所罗门码)与其应用
    通信编码揭秘:2.信道编码(汉明码、循环冗余校验码、里德所罗门码)与其应用摘要信道编码的目的是提高数据传输的可靠性,确保即使在噪声环境下传输的数据也能被正确接收。本文将探讨汉明码、循环冗余校验(CRC)和里德-所罗门码三种常见的信道编码方法,并通过实际例子说明它们的应用......
  • [JOISC 2023 Day3] Tourism
    虚树大小可以从两个角度进行思考:最小斯坦纳树大小,或者,子树内至少有一个标记点的点的数量减去虚树上边的点的数量。前者的优点是简洁,后者的优点是不依赖dfn序的排序。这道题在利用后者的同时,将赋值看作了颜色段,用树链剖分保证了颜色段总数为\(O(n\logn)\),利用了odt。#inc......
  • Docker容器时间与宿主机不一致/宿主机时间不同步校验
    一、Docker容器时间与宿主机不一致前言如果在启动Docker容器的过程中没有单独配置localtime,很可能造成Docker容器时间与主机时间不一致的情况,比如UTC和CST相差8小时,换句话来说就是容器时间与北京时间相差8个小时。问题描述问题:容器时间与北京时间相差8个小时   #查看主机时间......
  • SQL Zoo 7.More JOIN operations
    以下数据均来自SQLZoo1.Listthefilmswherethe yr is1962[Show id, title](列出1962年的电影)SELECTid,titleFROMmovieWHEREyr=19622.Giveyearof'CitizenKane'.(给出《公民凯恩》的年份)selectyrfrommoviewheretitle='CitizenKane'3.List......
  • [lnsyoj539/luoguP2120/ZJOI2007]仓库建设
    题意懒了(sol显然DP设计状态:\(f_i\)表示\(1\simi\)的工厂中,在第\(i\)个工厂处建设仓库的最小代价;状态转移:由题意,显然可得:\[f_i=\min_{j=1}^{i-1}\{f_j+c_i+\sum_{k=j+1}^i(x_i-x_k)\cdotp_k\}\]我们发现中间的一坨求和可以通过前缀和的方式预处理出\(sum_i=......
  • Spring Boot 中使用 JSON Schema 来校验复杂JSON数据
    JSON是我们编写API时候用于数据传递的常用格式,那么你是否知道JSONSchema呢?在数据交换领域,JSONSchema以其强大的标准化能力,为定义和规范JSON数据的结构与规则提供了有力支持。通过一系列精心设计的关键字,JSONSchema能够详尽地描述数据的各项属性。然而,仅凭JSONSchema......
  • Element el-form 表单校验,保存或提交验证某一项或者多项;validateField 的使用
    通常新增或者编辑对form表单的校验都是全局性的校验:this.$refs.form.validate(valid=>{if(valid){//校验通过,业务逻辑代码...}});如果需要对表单里的特定几个必填项进行校验,应该如何实现? 业务场景:下图点击保存按钮时,只需要校验前两项,其余参数不......
  • Spring Boot 中使用 JSON Schema 来校验复杂JSON数据
    JSON是我们编写API时候用于数据传递的常用格式,那么你是否知道JSONSchema呢?在数据交换领域,JSONSchema以其强大的标准化能力,为定义和规范JSON数据的结构与规则提供了有力支持。通过一系列精心设计的关键字,JSONSchema能够详尽地描述数据的各项属性。然而,仅凭JSONSchema本......