Has*_*rki 5 javascript api server-side-validation class-validator nestjs
我想在我的 CRUD API 上应用服务器端验证。有问题的实体称为Employee。我正在使用employee.dto(如下所示)作为创建和更新端点。
class-validator 包在该create方法上运行良好,但当我Partial<EmployeeDTO>在 update 方法中使用它时会忽略 DTO 中的所有规则。
请使用下面的代码作为参考。
"class-transformer": "^0.2.3",
"class-validator": "^0.10.0",
Run Code Online (Sandbox Code Playgroud)
import { IsString, IsNotEmpty, IsEmail, IsEnum } from 'class-validator';
import { EmployeeRoles } from '../../entities/employee.entity';
export class EmployeeDTO {
@IsString()
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
@IsString()
@IsNotEmpty()
username: string;
@IsString()
@IsNotEmpty()
fullName: string;
@IsString()
@IsNotEmpty()
@IsEnum(EmployeeRoles)
role: string;
}
Run Code Online (Sandbox Code Playgroud)
import {
Controller,
Param,
Post,
Body,
Put,
UsePipes,
} from '@nestjs/common';
import { EmployeeDTO } from './dto/employee.dto';
import { EmployeeService } from './employee.service';
import { ValidationPipe } from '../shared/pipes/validation.pipe';
@Controller('employee')
export class EmployeeController {
constructor(private employeeService: EmployeeService) {}
@Post()
@UsePipes(ValidationPipe)
addNewEmployee(@Body() data: EmployeeDTO) {
return this.employeeService.create(data);
}
@Put(':id')
@UsePipes(ValidationPipe)
updateEmployee(@Param('id') id: number, @Body() data: Partial<EmployeeDTO>) {
return this.employeeService.update(id, data);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以想到的解决方法是为create和update方法创建单独的 DTO ,但我不喜欢重复代码的想法。
对于这个答案,我将进行猜测并假设您使用NestJS 文档中ValidationPipe提供的或近似派生的。
您的updateEmployee方法的参数data类型是Partial,它不会发出任何类型元数据。用于ValidationPipe使用实例化class-transformer模块,导致中class-validator模块来验证一个普通的对象,而不是一个EmployeeDTO。
为了验证工作,data参数的类型应该是一个类。您可以创建单独的 DTO 来创建和更新您的实体,或者如果您想保留一个类,则使用验证组。
为了实现部分验证,您可以使用PartialType效用函数。你可以在这里阅读:https :
//docs.nestjs.com/openapi/mapped-types#partial
您需要创建另一个类:
export class UpdateEmployeeDTO extends PartialType(EmployeeDTO) {}
Run Code Online (Sandbox Code Playgroud)
然后在你的控制器,你需要更换的类型@Body data Partial<EmployeeDTO>来UpdateEmployeeDto。它应该是这样的:
@Patch(':id')
@UsePipes(ValidationPipe)
updateEmployee(@Param('id') id: number, @Body() data: UpdateEmployeeDTO) {
return this.employeeService.update(id, data);
}
Run Code Online (Sandbox Code Playgroud)
请记住,你应该导入PartialType来自@nestjs/mapped-types不@nestjs/swagger一样的文档中建议。可以在此处找到有关此的更多信息
我遇到了这个问题,想分享一些发现和解决方案。
让我们假设我们有这个类:
User {
@IsNotEmpty()
name: string;
@IsOptional()
nickname?: string;
}
Run Code Online (Sandbox Code Playgroud)
在 POST 上,您希望应用所有验证。在 PATCH(或 PUT)上,您只希望验证应用于指定的属性。好了,POST没问题,在控制器中做:
@Body() params: User
Run Code Online (Sandbox Code Playgroud)
但是,PATCH(或 PUT)是有问题的,因为如果不普遍应用它,就无法处理开箱即用的部分验证。
以下是我探索的一些解决方案:
import { OmitType, PartialType } from '@nestjs/swagger';
UpdateUser extends PartialType(User) {}
Run Code Online (Sandbox Code Playgroud)
然后,在控制器中:
@Body() params: UpdateUser,
Run Code Online (Sandbox Code Playgroud)
使用 NestJS 的 PartialType 不会覆盖父类上的类验证器注释。因此,如果您@IsNotEmpty()在父类上有一个,它仍然会成为必需的。
在控制器中:
@Body() params: Partial<User>
Run Code Online (Sandbox Code Playgroud)
类验证器注释均不适用。这是因为不支持泛型,如其他地方所述。
在控制器中:
@Body() params: any
Run Code Online (Sandbox Code Playgroud)
然后,在控制器方法中validate()直接调用类验证器,如下所示:
const transformedValue = plainToClassFromExist(new User(), params);
const errors = validate(transformedValue, {
skipUndefinedProperties: true
});
Run Code Online (Sandbox Code Playgroud)
这里的神奇之处在于skipUndefinedProperties。如果该属性不存在(即未定义),则类验证器将不会验证它。请注意,@IsDefined() 绕过了这一点。
这可行,但会导致大量重复代码。
创建一个名为的装饰器PartialBody,从增强器读取类型信息,然后运行验证,请参阅https://gist.github.com/josephdpurcell/d4eff886786d58f58b86107c0947e19e作为示例。
确保全局验证管道中的 validateCustomDecorators=false 。
现在,它的使用方法有一些变化:
在控制器中:
@PartialBody(User) params: Partial<User>
Run Code Online (Sandbox Code Playgroud)
运行时PartialBody它将从传递的参数中检索类型。这只会验证存在的属性,并确保params是用户的一部分。好哇!
在控制器中:
@PartialBody() params: Partial<User>
Run Code Online (Sandbox Code Playgroud)
这会失败,因为不支持泛型。当PartialBody装饰器逻辑运行时,它无法访问任何类型信息。
在控制器中:
@PartialBody() params: User
Run Code Online (Sandbox Code Playgroud)
虽然这会像变体 1 一样执行部分验证,但它会失败,因为params会被视为完整类型而不是部分类型。在编写代码时,您的编辑器会认为所有属性都存在,但实际上它们可能不存在。
使用 PartialType 创建一个新类型:
import { PartialType } from '@nestjs/swagger';
export class PartialUser extends PartialType(User) {}
Run Code Online (Sandbox Code Playgroud)
在控制器中:
@PartialBody() params: PartialUser
Run Code Online (Sandbox Code Playgroud)
这与变体 1 一样工作,但您必须创建一个附加类。如果您希望通过覆盖属性和重新声明装饰器来覆盖类验证器检查,这可能是需要的。
| 归档时间: |
|
| 查看次数: |
5888 次 |
| 最近记录: |