参数校验
我们经常要在方法调用时执行一些类型检查,参数转换的操作,Midway 提供了一种简单的能力来快速检查参数的类型,这个能力来源于 joi 。
相关信息:
描述 | |
---|---|
可用于标准项目 | ✅ |
可用于 Serverless | ✅ |
可用于一体化 | ✅ |
包含独立主框架 | ❌ |
包含独立日志 | ❌ |
背景
最常用参数校验的地方是 控制器(Controller),同时你也可以在任意的 Class 中使用这个能力。
我们以控制器(Controller)中使用为例,还是那个 user。
➜ my_midway_app tree
.
├── src
│ ├── controller
│ │ └── user.ts
│ ├── interface.ts
│ └── service
│ └── user.ts
├── test
├── package.json
└── tsconfig.json
普通情况下,我们从 body
上拿到所有 Post 结果,并进行一些校验。
// src/interface.ts
export interface User {
id: number;
firstName: string;
lastName: string;
age: number;
}
// src/controller/home.ts
import { Controller, Get, Provide } from '@midwayjs/core';
@Controller('/api/user')
export class HomeController {
@Post('/')
async updateUser(@Body() user: User) {
if (!user.id || typeof user.id !== 'number') {
throw new Error('id error');
}
if (user.age <= 30) {
throw new Error('age not match');
}
// xxx
}
}
如果每个方法都需要这么校验,会非常的繁琐。
针对这种情况,Midway 提供了 Validate 组件。 配合 @Validate
和 @Rule
装饰器,用来 快速定义校验的规则,帮助用户 减少这些重复的代码。
注意,从 v3 开始,@Rule
和 @Validate
装饰器从 @midwayjs/validate
中导出。
安装依赖
$ npm i @midwayjs/validate@3 --save
或者在 package.json
中增加如下依赖后,重新安装。
{
"dependencies": {
"@midwayjs/validate": "^3.0.0"
// ...
},
"devDependencies": {
// ...
}
}
开启组件
在 configuration.ts
中增加组件。
import { Configuration, App } from '@midwayjs/core';
import * as koa from '@midwayjs/koa';
import * as validate from '@midwayjs/validate';
import { join } from 'path';
@Configuration({
imports: [koa, validate],
importConfigs: [join(__dirname, './config')],
})
export class MainConfiguration {
@App()
app: koa.Application;
async onReady() {
// ...
}
}
定义检查规则
按照上面的逻辑,我们需要 重新定义一个新的 Class,因为装饰器只能装饰在实际的 Class 上,而不是 interface。
为了方便后续处理,我们将 user 放到一个 src/dto
目录中。
Data Transfer Object(数据传输对象)DTO 是一组需要跨进程或网络边界传输的聚合数据的简单容器。它不应该包含业务逻辑,并将其行为限制为诸如内部一致性检查和基本验证之类的活动。
// src/dto/user.ts
import { Rule, RuleType } from '@midwayjs/validate';
export class UserDTO {
@Rule(RuleType.number().required())
id: number;
@Rule(RuleType.string().required())
firstName: string;
@Rule(RuleType.string().max(10))
lastName: string;
@Rule(RuleType.number().max(60))
age: number;
}
由于这个类属于一个 PlainObject
,也不需要被依赖注入管理,我们不需要提供 @Provide
装饰器。
这个 User Class 提供了三个属性和他们对应的校验规则。
id
一个必填的数字类型firstName
一个必填的字符串类型lastName
一个可选的最长为 10 的字符串类型age
一个最大不超过 60 的数字
@Rule
装饰器用于 修饰需要被校验的属性,它的参数为 RuleType
对象提供的校验规则的链式方法。
这里的 RuleType
即为 joi 对象本身。
joi 提供了非常多的校验类型,还可以对对象和数组中的字段做校验,还有例如字符串常用的 RuleType.string().email()
,以及 RuleType.string().pattern(/xxxx/)
正则校验等,具体可以查询 joi 的 API 文档。
校验参数
定义完类型之后,就可以直接在业务代码中使用了。
// src/controller/home.ts
import { Controller, Get, Provide, Body } from '@midwayjs/core';
import { UserDTO } from './dto/user';
@Controller('/api/user')
export class HomeController {
@Post('/')
async updateUser(@Body() user: UserDTO) {
// user.id
}
}
所有的校验代码都通通不见了,业务变的更纯粹了,当然,记得要把原来的 user interface 换成 Class。
一旦校验失败,浏览器或者控制台就会报出类似的错误。
ValidationError: "id" is required
同时,由于定义了 id
的类型,在拿到字符串的情况下,会自动将 id 变为数字。
async updateUser(@Body() user: UserDTO ) {
// typeof user.id === 'number'
}
如果需要对方法级别单独配置信息,可以使用 @Validate
装饰器,比如单独配置错误状态。
// src/controller/home.ts
import { Controller, Get, Provide } from '@midwayjs/core';
import { Validate } from '@midwayjs/validate';
import { UserDTO } from './dto/user';
@Controller('/api/user')
export class HomeController {
@Post('/')
@Validate({
errorStatus: 422,
})
async updateUser(@Body() user: UserDTO) {
// user.id
}
}
一般情况下,使用全局默认配置即可。