0

我正在尝试使用转换将formData请求从字符串转换为json对象,然后使用validationPipe(类验证器)进行验证,但我得到了

Maximum call stack size exceeded
    at cloneObject (E:\projectos\Gitlab\latineo\latineo-apirest\node_modules\mongoose\lib\utils.js:290:21)
    at clone (E:\projectos\Gitlab\latineo\latineo-apirest\node_modules\mongoose\lib\utils.js:204:16)

尝试调试后,我进入我的控制器 3 次,对象保存在数据库中,但没有验证和内部 transformJSONToObject 9 次...

我的 main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  app.use(helmet());
  app.enableCors();
  app.use(
    rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 4000, // limit each IP to 100 requests per windowMs
    }),
  );
  app.use(compression());
  app.use('/upload', express.static(join(__dirname, '..', 'upload')));
  const options = new DocumentBuilder()
    .setTitle('XXX')
    .setDescription('XXX')
    .setVersion('1.0')
    .addBearerAuth()
    .build();
  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api', app, document);
  await app.listen(3000);
}
bootstrap();

我的nestjs dto

export class CreateRestaurantDto {
  // to do, check the documentation from class-validator for array of objects
  @IsString()
  @IsNotEmpty()
  @ApiModelProperty({ type: String })
  @Length(3, 100)
  readonly name: string;
  @IsString()
  @IsNotEmpty()
  @ApiModelProperty({ type: String })
  @Length(3, 500)
  readonly description: string;
  @Transform(transformJSONToObject, { toClassOnly: true })
  @ValidateNested()
  @ApiModelProperty({ type: [RestaurantsMenu] })
  readonly menu: RestaurantsMenu[];
  @Transform(transformJSONToObject, { toClassOnly: true })
  @IsString({
    each: true,
  })
  @IsNotEmpty({
    each: true,
  })
  @Length(3, 50, { each: true })
  @ApiModelProperty({ type: [String] })
  readonly type: string[];
  @Transform(transformJSONToObject, { toClassOnly: true })
  @ValidateNested()
  @ApiModelProperty({ type: [RestaurantsLocation] })
  readonly location: RestaurantsLocation[];
}

这是我的控制器

 @ApiBearerAuth()
  @UseGuards(JwtAuthGuard)
  @UseInterceptors(FilesInterceptor('imageUrls'))
  @ApiConsumes('multipart/form-data')
  @ApiImplicitFile({
    name: 'imageUrls',
    required: true,
    description: 'List of restaurants',
  })
  @Post()
  async createRestaurant(
    @Body() createRestaurantDto: CreateRestaurantDto,
    @UploadedFiles() imageUrls,
    @Req() request: any,
  ): Promise<RestaurantDocument> {
    const userId = request.payload.userId;
    const user = await this.usersService.findUserById(userId);
    const mapUrls = imageUrls.map(element => {
      return element.path;
    });
    const restaurant = {
      ...createRestaurantDto,
      imagesUrls: mapUrls,
      creator: user,
    };
 // just add a resturant to mongodb
    const createdRestaurant = await this.restaurantsService.addRestaurant(
      restaurant,
    );
    user.restaurants.push(createdRestaurant);
    user.save();
    return createdRestaurant;
  }
4

2 回答 2

3

我知道现在为时已晚:) 但也许这可以帮助某人

在 deep-parse-json 的帮助下,我的解决方案是创建一个ParseFormDataJsonPipe如下并将其放在ValidationPipe. 您可以在构造函数中传递表单数据属性的例外列表,以保留一些纯粹的东西,例如图像、二进制、jsonArray ...

import { PipeTransform, ArgumentMetadata } from '@nestjs/common';
import { deepParseJson } from 'deep-parse-json';
import * as _ from 'lodash';

type TParseFormDataJsonOptions = {
  except?: string[];
};

export class ParseFormDataJsonPipe implements PipeTransform {
  constructor(private options?: TParseFormDataJsonOptions) {}

  transform(value: any, _metadata: ArgumentMetadata) {
    const { except } = this.options;
    const serializedValue = value;
    const originProperties = {};
    if (except?.length) {
      _.merge(originProperties, _.pick(serializedValue, ...except));
    }
    const deserializedValue = deepParseJson(value);
    console.log(`deserializedValue`, deserializedValue);
    return { ...deserializedValue, ...originProperties };
  }
}

这是控制器的示例

  @Post()
  @ApiBearerAuth('admin')
  @ApiConsumes('multipart/form-data')
  @UseGuards(JwtAuthGuard, RoleGuard)
  @Roles(Role.Admin)
  @UseInterceptors(
    FileInterceptor('image', {
      limits: { fileSize: imgurConfig.fileSizeLimit },
    }),
  )
  async createProductByAdmin(
    @Body(
      new ParseFormDataJsonPipe({ except: ['image', 'categoryIds'] }),
      new ValidationPipe(),
    )
    createDto: CreateProductDto,
    @UploadedFile() image: Express.Multer.File,
  ) {
    console.log(`createDto`, createDto);
  }
于 2021-05-07T18:36:13.760 回答
1

您可以将类转换器库用于您的 DTO

import { Transform, Type } from 'class-transformer';
  1. 对于对象:

  @Transform(({ value }) => JSON.parse(value))
  name: { small:string, middle:string, big:string };
    
  1. 对于数字:

  @Type(() => Number)
  count number;
于 2021-08-23T10:20:00.910 回答