NestJS使用class-validator進(jìn)行數(shù)據(jù)驗(yàn)證
前言
在現(xiàn)代Web開發(fā)中,數(shù)據(jù)驗(yàn)證是必不可少的一環(huán),它不僅能夠確保數(shù)據(jù)的準(zhǔn)確性,還能提高系統(tǒng)的安全性。在使用NestJS框架進(jìn)行項(xiàng)目開發(fā)時(shí),class-validator與class-transformer這兩個(gè)庫為我們提供了方便的數(shù)據(jù)驗(yàn)證解決方案。
本文將通過詳細(xì)的步驟和實(shí)戰(zhàn)技巧,帶大家掌握如何在NestJS中使用class-validator進(jìn)行數(shù)據(jù)驗(yàn)證。通過這篇文章,你將能夠?qū)W會如何使用class-validator優(yōu)雅的實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證,以及11條實(shí)戰(zhàn)中常用的驗(yàn)證技巧,提高項(xiàng)目的數(shù)據(jù)校驗(yàn)?zāi)芰Α?/p>
使用步驟
第一步:安裝 class-validator 和 class-transformer
要使用 class-validator,需要安裝兩個(gè)庫:class-validator 和 class-transformer。
npm install class-validator class-transformer
第二步:創(chuàng)建 DTO(數(shù)據(jù)傳輸對象)
在 NestJS 中,通常使用 DTO(Data Transfer Object)來定義請求數(shù)據(jù)的結(jié)構(gòu)。首先,需要創(chuàng)建一個(gè)用于用戶注冊的 DTO 類,并使用 class-validator 的裝飾器來定義驗(yàn)證規(guī)則。
// src/user/dto/create-user.dto.ts import { IsString, IsEmail, IsNotEmpty, Length } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty() @Length(4, 20) username: string; @IsEmail() email: string; @IsString() @IsNotEmpty() @Length(8, 40) password: string; }
在這個(gè) DTO 中,定義了三個(gè)字段:username、email 和 password,并使用 class-validator 的裝飾器指定了驗(yàn)證規(guī)則。
第三步:使用管道驗(yàn)證數(shù)據(jù)
接下來,需要在控制器中使用 DTO,并通過 NestJS 的管道(Pipes)來驗(yàn)證傳入的數(shù)據(jù)。
// src/user/user.controller.ts import { Controller, Post, Body } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; @Controller('user') export class UserController { @Post('register') async register(@Body() createUserDto: CreateUserDto) { // 處理注冊邏輯 return { message: 'User registered successfully', data: createUserDto }; } }
在這個(gè)例子中,在 register 方法中使用了 @Body() 裝飾器來獲取請求體,并傳入了 CreateUserDto。NestJS 會自動驗(yàn)證該 DTO,如果驗(yàn)證失敗,將拋出異常并返回適當(dāng)?shù)腻e(cuò)誤響應(yīng)。
第四步:全局啟用驗(yàn)證管道
為了更方便地管理,可以全局啟用驗(yàn)證管道,這樣所有的 DTO 驗(yàn)證都會自動進(jìn)行。
// src/main.ts import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); await app.listen(3000); } bootstrap();
在 main.ts 文件中,使用 ValidationPipe 全局啟用了驗(yàn)證管道。這樣一來,無論在哪個(gè)控制器中使用 DTO,NestJS 都會自動進(jìn)行數(shù)據(jù)驗(yàn)證。當(dāng)然也可以僅對某些控制器開啟驗(yàn)證管道,詳情參考下方實(shí)戰(zhàn)技巧。
實(shí)戰(zhàn)使用技巧
1. 局部驗(yàn)證管道
可以為特定的路由或控制器方法配置驗(yàn)證管道,而無需全局啟用。這樣可以在不同的場景下靈活使用不同的驗(yàn)證規(guī)則。
import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; @Controller('user') export class UserController { @Post('register') @UsePipes(new ValidationPipe({ transform: true, whitelist: true, forbidNonWhitelisted: true, })) async register(@Body() createUserDto: CreateUserDto) { // 處理注冊邏輯 return { message: 'User registered successfully', data: createUserDto }; } }
2. 自定義錯(cuò)誤消息
class-validator 允許為每個(gè)驗(yàn)證規(guī)則定義自定義錯(cuò)誤消息。例如:
import { IsString, IsNotEmpty, Length, IsEmail } from 'class-validator'; export class CreateUserDto { @IsString({ message: '用戶名必須是字符串' }) @IsNotEmpty({ message: '用戶名不能為空' }) @Length(4, 20, { message: '用戶名長度必須在4到20個(gè)字符之間' }) username: string; @IsEmail({}, { message: '郵箱格式不正確' }) email: string; @IsString({ message: '密碼必須是字符串' }) @IsNotEmpty({ message: '密碼不能為空' }) @Length(8, 40, { message: '密碼長度必須在8到40個(gè)字符之間' }) password: string; }
3. 嵌套對象驗(yàn)證
如果 DTO 中包含嵌套對象,可以使用 @ValidateNested() 裝飾器進(jìn)行驗(yàn)證。例如:
import { Type } from 'class-transformer'; import { ValidateNested, IsString, IsNotEmpty } from 'class-validator'; class AddressDto { @IsString() @IsNotEmpty() street: string; @IsString() @IsNotEmpty() city: string; } export class CreateUserDto { @IsString() @IsNotEmpty() username: string; @ValidateNested() @Type(() => AddressDto) address: AddressDto; }
4. 組合驗(yàn)證裝飾器
有時(shí)可能需要將多個(gè)驗(yàn)證規(guī)則組合在一起,這時(shí)可以使用 @ValidatorConstraint() 來創(chuàng)建自定義驗(yàn)證裝飾器。例如:
判斷數(shù)據(jù)庫中是否已經(jīng)存在用戶名
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; @ValidatorConstraint({ async: false }) export class IsUsernameUniqueConstraint implements ValidatorConstraintInterface { validate(username: any) { // 這里可以添加驗(yàn)證邏輯,例如查詢數(shù)據(jù)庫 return true; // 如果驗(yàn)證通過返回 true } } export function IsUsernameUnique(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [], validator: IsUsernameUniqueConstraint, }); }; } // 使用自定義裝飾器 export class CreateUserDto { @IsUsernameUnique({ message: '用戶名已存在' }) username: string; }
2.驗(yàn)證密碼強(qiáng)度
import { registerDecorator, ValidationOptions, ValidationArguments } from 'class-validator'; export function IsStrongPassword(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ name: 'isStrongPassword', target: object.constructor, propertyName: propertyName, options: validationOptions, validator: { validate(value: any, args: ValidationArguments) { return /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/.test(value); }, defaultMessage(args: ValidationArguments) { return '密碼必須包含大小寫字母、數(shù)字和特殊字符,并且至少8個(gè)字符長'; }, }, }); }; } export class ChangePasswordDto { @IsStrongPassword({ message: '密碼不符合強(qiáng)度要求' }) newPassword: string; }
3.條件驗(yàn)證
根據(jù)條件進(jìn)行驗(yàn)證,可以使用 @ValidateIf 裝飾器。
import { ValidateIf, IsNotEmpty, IsEmail } from 'class-validator'; export class UpdateUserDto { @IsEmail() email: string; @ValidateIf(o => o.email) @IsNotEmpty({ message: '新郵件地址不能為空' }) newEmail: string; }
4.使用 @Matches 進(jìn)行正則表達(dá)式驗(yàn)證
使用 @Matches 裝飾器,可以驗(yàn)證字符串是否與指定的正則表達(dá)式匹配。
import { Matches } from 'class-validator'; export class ChangePasswordDto { @Matches(/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}/, { message: '密碼必須包含大小寫字母、數(shù)字和特殊字符,并且至少8個(gè)字符長' }) newPassword: string; }
5.全局驗(yàn)證選項(xiàng)
全局啟用驗(yàn)證管道時(shí),可以配置全局驗(yàn)證選項(xiàng),比如剝離非白名單字段、自動轉(zhuǎn)換類型等。
// src/main.ts import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true, // 剝離非白名單字段 forbidNonWhitelisted: true, // 禁止非白名單字段 transform: true, // 自動轉(zhuǎn)換類型 })); await app.listen(3000); } bootstrap();
6.動態(tài)驗(yàn)證消息
有時(shí)可能需要根據(jù)具體的驗(yàn)證條件動態(tài)生成錯(cuò)誤消息,可以使用 ValidationArguments 來實(shí)現(xiàn)。
import { IsString, MinLength, ValidationArguments } from 'class-validator'; export class CreateUserDto { @IsString() @MinLength(4, { message: (args: ValidationArguments) => { return `用戶名太短了,至少需要 ${args.constraints[0]} 個(gè)字符`; }, }) username: string; }
7.@Validate 自定義驗(yàn)證邏輯
如果內(nèi)置裝飾器無法滿足需求,可以使用 @Validate 裝飾器添加自定義驗(yàn)證邏輯。
import { Validate, ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; @ValidatorConstraint({ name: 'customText', async: false }) class CustomTextConstraint implements ValidatorConstraintInterface { validate(text: string, args: ValidationArguments) { return text.startsWith('prefix_'); // 任何自定義邏輯 } defaultMessage(args: ValidationArguments) { return '文本 ($value) 必須以 "prefix_" 開頭'; } } export class CustomTextDto { @Validate(CustomTextConstraint) customText: string; }
8.屬性分組驗(yàn)證
通過分組,可以在不同情境下驗(yàn)證不同的字段。比如在創(chuàng)建和更新時(shí)可能需要驗(yàn)證不同的字段。
import { IsString, IsNotEmpty } from 'class-validator'; export class CreateUserDto { @IsString() @IsNotEmpty({ groups: ['create'] }) username: string; @IsString() @IsNotEmpty({ groups: ['create', 'update'] }) password: string; } // 使用時(shí)指定組 import { ValidationPipe } from '@nestjs/common'; const createUserValidationPipe = new ValidationPipe({ groups: ['create'] }); const updateUserValidationPipe = new ValidationPipe({ groups: ['update'] }); 在控制器中使用不同的管道進(jìn)行驗(yàn)證: import { Controller, Post, Put, Body, UsePipes } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { createUserValidationPipe, updateUserValidationPipe } from './validation-pipes'; @Controller('user') export class UserController { @Post('create') @UsePipes(createUserValidationPipe) async createUser(@Body() createUserDto: CreateUserDto) { // 處理創(chuàng)建用戶邏輯 return { message: 'User created successfully', data: createUserDto }; } @Put('update') @UsePipes(updateUserValidationPipe) async updateUser(@Body() updateUserDto: CreateUserDto) { // 處理更新用戶邏輯 return { message: 'User updated successfully', data: updateUserDto }; } }
9.僅執(zhí)行部分屬性驗(yàn)證
有時(shí)可能需要只驗(yàn)證對象的一部分屬性,可以使用 PartialType 來實(shí)現(xiàn)。
import { PartialType } from '@nestjs/mapped-types'; export class UpdateUserDto extends PartialType(CreateUserDto) {} //以下是如何在控制器中使用 UpdateUserDto。 // src/user/user.controller.ts import { Controller, Post, Put, Body, Param } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; @Controller('user') export class UserController { @Post('create') async createUser(@Body() createUserDto: CreateUserDto) { // 處理創(chuàng)建用戶邏輯 return { message: 'User created successfully', data: createUserDto }; } @Put('update/:id') async updateUser(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { // 處理更新用戶邏輯 return { message: 'User updated successfully', data: updateUserDto }; } }
在這個(gè)示例中,UpdateUserDto 繼承自 PartialType(CreateUserDto),這意味著 UpdateUserDto 包含 CreateUserDto 中的所有屬性,但這些屬性都是可選的。這在更新操作中非常有用,因?yàn)槲覀兛赡苤幌胩峁┠切┬枰碌淖侄?,而不是所有字段?/p>
10.驗(yàn)證消息的國際化
通過使用自定義驗(yàn)證裝飾器和消息生成函數(shù),可以實(shí)現(xiàn)驗(yàn)證消息的國際化。
import { IsString, IsNotEmpty, Length, ValidationArguments } from 'class-validator'; import { i18n } from 'i18next'; // 假設(shè)在項(xiàng)目中使用 i18n export class CreateUserDto { @IsString() @IsNotEmpty({ message: (args: ValidationArguments) => i18n.t('validation.usernameRequired') }) @Length(4, 20, { message: (args: ValidationArguments) => i18n.t('validation.usernameLength', { min: 4, max: 20 }) }) username: string; }
總結(jié)
使用 class-validator 結(jié)合 NestJS,可以讓輕松地在應(yīng)用中進(jìn)行數(shù)據(jù)驗(yàn)證,不僅提高了代碼的可讀性,還保證了數(shù)據(jù)的準(zhǔn)確性和安全性。
以上就是NestJS使用class-validator進(jìn)行數(shù)據(jù)驗(yàn)證的詳細(xì)內(nèi)容,更多關(guān)于NestJS數(shù)據(jù)驗(yàn)證的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript+HTML?實(shí)現(xiàn)網(wǎng)頁錄制音頻與下載
在這個(gè)數(shù)字化的時(shí)代,網(wǎng)頁端的音頻處理能力已經(jīng)成為一個(gè)非常熱門的需求,本文將詳細(xì)介紹如何利用 getUserMedia 和 MediaRecorder 這兩個(gè)強(qiáng)大的 API,實(shí)現(xiàn)網(wǎng)頁端音頻的錄制、處理和播放等功能,需要的朋友可以參考下2024-07-07淺談js對象屬性 通過點(diǎn)(.) 和方括號([]) 的不同之處
下面小編就為大家?guī)硪黄獪\談js對象屬性 通過點(diǎn)(.) 和方括號([]) 的不同之處。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10js判斷鼠標(biāo)同時(shí)離開兩個(gè)div的思路及代碼
js判斷鼠標(biāo)同時(shí)離開兩個(gè)div想了好長時(shí)間終于出爐了,下面與大家分享下具體的實(shí)現(xiàn)代碼,感興趣的朋友可以參考下啊2013-05-05javascript自動生成包含數(shù)字與字符的隨機(jī)字符串
這篇文章主要介紹了javascript自動生成包含數(shù)字與字符的隨機(jī)字符串,涉及Math.random()和Math.floor()兩個(gè)函數(shù)的使用技巧,需要的朋友可以參考下2015-02-02JavaScript解析任意形式的json樹型結(jié)構(gòu)展示
這篇文章主要介紹了JavaScript解析任意形式的json樹型結(jié)構(gòu)展示的相關(guān)資料,需要的朋友可以參考下2017-07-07原生javascript實(shí)現(xiàn)圖片輪播切換效果
這篇文章主要為大家詳細(xì)介紹了原生javascript實(shí)現(xiàn)圖片輪播切換效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07