NestJS搭配PostgreSQL创建用户前校验用户名是否存在的实现方法
NestJS 实现用户名重复校验方案
你用class-transformer找不到对应校验能力是正常的:这个库只负责普通对象与类实例的转换,不提供校验规则能力,重复名校验需要配合校验库+业务逻辑判断+数据库约束三层实现,从根本上避免重复用户创建。
第一步:安装依赖并开启全局校验
首先安装NestJS参数校验所需的依赖:
npm install class-validator class-transformer
在项目入口main.ts中全局开启参数校验管道,否则DTO上的校验规则不会生效:
// main.ts import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // 全局启用参数校验 app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); } bootstrap();
第二步:添加数据库层唯一约束(核心兜底)
仅靠业务层查重无法覆盖高并发场景(两个请求同时判断用户名不存在、同时插入就会产生重复数据),必须先给username字段加数据库级唯一约束。
修改用户实体类:
// user.entity.ts import { Entity, PrimaryGeneratedColumn, Column, Unique } from 'typeorm'; @Entity() @Unique(['username']) // 给用户名字段加唯一索引约束 export class users_trans { @PrimaryGeneratedColumn() id: number; @Column() username: string; @Column() password: string; }
如果是手动维护表结构,执行如下SQL添加约束即可:
ALTER TABLE users_trans ADD CONSTRAINT uk_username UNIQUE (username);
第三步:编写创建用户的参数DTO
不要在Controller中直接用any接收请求参数,新建DTO类定义参数规则:
// create-user.dto.ts import { IsNotEmpty, IsString } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty({ message: '用户名不能为空' }) username: string; @IsString() @IsNotEmpty({ message: '密码不能为空' }) password: string; }
第四步:修改Service层增加查重逻辑
在创建用户前先查询用户名是否存在,同时捕获数据库唯一约束冲突的错误做兜底处理:
// user.service.ts import { Injectable, ConflictException } from '@nestjs/common'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; import { users_trans } from './users.entity'; import { CreateUserDto } from './create-user.dto'; @Injectable() export class UsersService { constructor(@InjectRepository(users_trans) private usersRepo: Repository<users_trans>) {} findAll(): Promise<users_trans[]> { return this.usersRepo.find(); } async create(newUser: CreateUserDto){ // 业务层先查重 const existUser = await this.usersRepo.findOne({ where: { username: newUser.username } }); if (existUser) { throw new ConflictException('用户名已存在,无法重复创建'); } try { return await this.usersRepo.insert(newUser); } catch (err) { // PostgreSQL唯一约束冲突固定错误码为23505,兜底处理并发场景的重复请求 if (err.code === '23505') { throw new ConflictException('用户名已存在,无法重复创建'); } throw err; } } async update(usertoUpdate){ return this.usersRepo.update(usertoUpdate.id,usertoUpdate); } async delete(id){ return this.usersRepo.delete(id); } }
第五步:修改Controller层用DTO接收参数
替换原有的any类型,让参数校验规则生效:
// user.controller.ts import {UsersService} from './users.service'; import {users_trans} from './users.entity'; import { Controller, Get, Post, Body, Delete, Param, HttpCode } from '@nestjs/common'; import { CreateUserDto } from './create-user.dto'; @Controller('users') export class UserController { constructor(private readonly usersService: UsersService){} @Get('all') async getAll():Promise<users_trans[]>{ return await this.usersService.findAll(); } @Post('add') @HttpCode(201) async createUser(@Body() newUser: CreateUserDto){ return await this.usersService.create(newUser); } @Post('update') @HttpCode(201) async updateUser(@Body() usertoUpdate:any){ return this.usersService.update(usertoUpdate); } @Delete('delete/:id') @HttpCode(200) async deleteEmployee(@Param('id') id){ return this.usersService.delete(id); } }
注意点
- 原有代码中数据库操作方法没有加
await,会导致请求返回时数据库操作还未执行,异常也无法正常抛出给前端,上述代码已经补全异步写法。 - 禁止省略数据库唯一约束,仅靠业务层查重在高并发下必然出现重复数据。
内容的提问来源于stack exchange,提问作者HardCodingFelix
相关产品推荐
相关产品推荐

