最近很是烦躁,有一个问题,Nest本身是没有自己的校验的规则的,它依赖的是class-validator,这玩意有很多的内置的校验装饰器,可以在dto中进行使用,但是有一个问题,那就是,这玩意没有结合数据库数据,例如我们想要用户注册的时候校验我们的数据库,保证用户的唯一性,完了它没有...
怎么办?
我也是小白,所以我就给大家提供一下我的思路哈
首先,我想,他不就是个包吗,直接npm上看看去,这里其实也有他的github的地址,你也可以去github上看,我们去吧,npm 翻译成中文,他连代码都给你翻译了......
来到github,你可以使用谷歌翻译,翻译一下,反正我是看不懂
这里有它的目录,从安装到高阶,都有使用的方法哈
这里我们看到了,他自己提供了很多的验证装饰器,但是就是没有我们验证用户唯一性的,所以,我们直接看它的自定义验证类以及自定义验证装饰器哈
就从这里开始,一眼就能看出来了.所以我们自己写一个吧
项目配置
- 提前搭建一个nest项目
- 配置prisma连接数数据库
- 使用docker创建数据库
- 创建一个auth模块资源,包含控制器,服务,模块以及dto
我的目录如下,这些是我们必须的内容哈
自定义验证类
我们在common里的validators文件中写我们的自定义的验证类
这里我们依照官方的文档自己写一下哈
必须的内容:
- 完全实现ValidatorConstraintInterface接口的validate方法
使用@ValidatorConstraint
装饰器标记了我们的自定义类- 我们的dto验证的那个字段,validate的第一个参数就是什么,这里我们使用name(使用prisma中的findUnique方法,必须保证我们的name字段在数据库中是唯一的)
- 注入parsma服务,用来在数据库中查询数据
- 配置默认消息,避免开发者不传递错误信息
有人会问,我们的
args: ValidationArguments是什么?其实这个就是我们的当前的dto的信息哈,它包含我们当前的数据,字段名.dto等信息
// is-user-already-exist.validator.ts
import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
import { PrismaService } from '../../../prisma/prisma.service';
import { Injectable } from '@nestjs/common';
@ValidatorConstraint({ async: true })
@Injectable()
export class IsUserAlreadyExist implements ValidatorConstraintInterface {
// 这里我们拿到prisma,获取用户
constructor(private readonly prisma: PrismaService) {
}
// 传一个name即可
async validate(name: string, args: ValidationArguments) {
console.log(`你现在验证的用户是${args.value}`);
// 查询用户
const user = await this.prisma.user.findUnique({
where: {
name,
},
});
return !user;// true 表示验证通过(即不存在),false 表示验证失败(存在)
}
// 配置默认消息
defaultMessage(args: ValidationArguments): string {
return `User with name ${args.value} already exists`;
}
}
其实到这里,我们写的自定义验证类已经可以使用了
我们只需要把他注入到module的providers中,然后再dto中使用即可,注意,你只能使用
@Validate(IsUserAlreadyExist)的格式,不可以使用 @IsUserAlreadyExist,因为你还没有把他封装为装饰器呢
@Validate(IsUserAlreadyExist)
当你使用的时候,你会发现用不了,这里就是我踩的坑了,原因是什么?就是你官当文档没有看完呗
你直接使用我们的自定义验证类是无法使用的,他会提示错译,意思就是无法获取到user,意思就是他拿不到prisma服务了,为什么?因为他不在nest的IOC内,所以,你看文档提示的
这里才是最关键的
自定义验证装饰器
如何直接封装为装饰器呢?官方也给出了答案,所以我们直接爪照抄
核心部分:
- 暴露一个函数,函数名作为装饰器名
- 接收ValidationOptions作为参数,这是必须的,否则你无法使用
validationOptions的错误信息等
- validator必须使用我们刚刚写的自定义验证类
import { registerDecorator, ValidationOptions } from 'class-validator';
import { IsUserAlreadyExist } from '../validators/is-user-already-exist.validator';
export function IsUserAlreadyExistDecorator(validationOptions?: ValidationOptions) {
return function(object: object, propertyName: string) {
registerDecorator({
name: 'IsUserAlreadyExist',
target: object.constructor,
propertyName,
options: validationOptions,
validator: IsUserAlreadyExist,
});
};
}
其实到这里还没有结束,因为我们这个装饰器和自定义验证类之只能服务于我们的name校验,那么email校验呢?其他的唯一性校验呢?所以请期待一下我们的通用唯一性装饰器,其实也很简单