如何访问未实例化类(DTO)中仅定义类型未赋值的属性?
验证请求体属性是否属于DTO的解决方案
核心问题:TypeScript类的属性仅为类型标注,编译为JavaScript后不会自动存在于类原型或实例中,因此直接通过CreateUserDTO.prototype.birthAt这类方式无法获取属性列表。
方法一:使用NestJS官方ValidationPipe(推荐)
NestJS的ValidationPipe自带白名单功能,可直接实现“请求体属性必须属于DTO”的验证,无需自定义逻辑:
在main.ts中全局配置管道:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true, // 自动移除DTO中未定义的属性 forbidNonWhitelisted: true, // 若存在DTO外的属性,直接抛出400错误 transform: true, // 自动将请求体转换为DTO实例(可选,增强类型安全) })); await app.listen(3000); } bootstrap();
开启forbidNonWhitelisted后,只要请求体包含DTO未定义的属性,就会直接返回错误,完全满足需求。
方法二:自定义逻辑(通过反射/Class-Transformer获取DTO属性)
若需自定义验证逻辑,可借助class-transformer的元数据存储来获取DTO的所有属性:
1. 获取DTO属性列表
import { getMetadataStorage } from 'class-transformer'; function getDTOPropertyNames(dtoClass: any): string[] { const metadataStorage = getMetadataStorage(); const targetMetadata = metadataStorage.getTargetMetadata(dtoClass); return targetMetadata ? targetMetadata.map(item => item.propertyName) : []; } // 示例调用 const userDTOProps = getDTOPropertyNames(CreateUserDTO); console.log(userDTOProps); // 输出 ['name', 'email', 'password', 'birthAt']
2. 在自定义Interceptor中验证
假设你已通过自定义装饰器将DTO类存入执行上下文的元数据中,可在Interceptor中对比属性:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, BadRequestException } from '@nestjs/common'; import { Observable } from 'rxjs'; import { getMetadataStorage } from 'class-transformer'; @Injectable() export class DTOPropertyValidationInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const request = context.switchToHttp().getRequest(); const requestBody = request.body; // 从元数据中获取目标DTO类(需提前通过自定义装饰器设置) const targetDTO = Reflect.getMetadata('target-dto', context.getHandler()); // 获取DTO的所有属性名 const metadataStorage = getMetadataStorage(); const dtoProps = metadataStorage.getTargetMetadata(targetDTO)?.map(item => item.propertyName) || []; const bodyProps = Object.keys(requestBody); // 找出请求体中不属于DTO的属性 const invalidProps = bodyProps.filter(prop => !dtoProps.includes(prop)); if (invalidProps.length > 0) { throw new BadRequestException(`请求体包含无效属性:${invalidProps.join(', ')}`); } return next.handle(); } }
补充说明
TypeScript类的属性仅在编译时做类型检查,编译为JS后不会自动挂载到类或原型上,因此直接访问CreateUserDTO.prototype.birthAt会返回undefined。必须通过元数据(如class-validator/class-transformer存储的装饰器元数据)来获取属性信息。
内容的提问来源于stack exchange,提问作者Yuri Silva
相关产品推荐
相关产品推荐

