简体   繁体   English

NestJs:如何让 Body 输入形状与实体的 DTO 不同?

[英]NestJs: How to have Body input shape different from entity's DTO?

I have DTOs for my Photo and Tag objects that look like this:我的照片和标签对象有 DTO,如下所示:

export class PhotoDto {
    readonly title: string
    readonly file: string
    readonly tags: TagDto[]
}

export class TagDto {
    readonly name: string
}

I use the PhotoDto in my photo.service.ts and eventually in the photo.controller.ts for the creation of Photo:我用的是PhotoDtophoto.service.ts并最终在photo.controller.ts为创建照片:

// In photo.service.ts
async create(createPhotoDto: PhotoDto): Promise<PhotoEntity> {
   // ...
   return await this.photoRepo.create(createPhotoDto)
}

// In photo.controller.ts
@Post()
async create(@Body() createPhotoDto: PhotoDto): Promise<PhotoEntity> {
   // ...
}

However, the input in the Body of the API is expected to have this structure:但是,API 主体中的输入应具有以下结构:

{
   "title": "Photo Title",
   "file": "/some/path/file.jpg",
   "tags": [
      {
         "name": "holiday"
      },
      {
         "name": "memories"
      }
   ]
}

How can I change the input shape of the Body to accept this structure instead?如何更改Body的输入形状以接受此结构?

{
   "title": "Photo Title",
   "file": "/some/path/file.jpg",
   "tags": ["holiday", "memories"]
}

I have tried creating 2 different DTOs, a CreatePhotoDto and an InputPhotoDto , one for the desired input shape in the controller and one for use with the service and entity, but this ends up very messy because there is a lot of work with converting between the 2 DTOs.我尝试创建 2 个不同的 DTO,一个CreatePhotoDto和一个InputPhotoDto ,一个用于控制器中所需的输入形状,另一个用于服务和实体,但这最终非常混乱,因为在2 个 DTO。

What is the correct way to have a different input shape from the Body of a Post request and then have it turned into the DTO required for use by the entity?什么是有从不同的输入形状的正确方法Body一的Post请求,然后它已经变成了需要通过实体使用DTO?

You can use the auto-transform of the ValidationPipe() :您可以使用ValidationPipe()的自动转换:

1) Add the ValidationPipe to your controller: 1) 将ValidationPipe添加到您的控制器中:

@UsePipes(new ValidationPipe({ transform: true }))
@Post()
async create(@Body() createPhotoDto: PhotoDto): Promise<PhotoEntity> {
   // ...
}

2) Add a @Transform to your PhotoDto : 2) 将@Transform添加到您的PhotoDto

// Transforms string[] to TagDto[]
const transformTags = tags => {
  if (Array.isArray(tags)) {
    return tags.map(tag => ({name: tag}))
  } else {
    return tags;
  }
}


import { Transform } from 'class-transformer';
export class PhotoDto {
    readonly title: string
    readonly file: string
    @Transform(transformTags, {toClassOnly: true})
    readonly tags: TagDto[]
}

Update DTO to将 DTO 更新为

export class PhotoDto { readonly title: string readonly file: string readonly tags: Array<string> }

It will change the API structure to它将 API 结构更改为

{ "title": "Photo Title", "file": "/some/path/file.jpg", "tags": ["holiday", "memories"] }

currently your tags property is an array of object of type TagDto , change tags property to just array of string.目前您的 tags 属性是一个TagDto类型的对象数组,将 tags 属性更改为字符串数组。

You can create a nest custom decorator to convert input data to your DTO object.您可以创建nest自定义装饰器以将输入数据转换为 DTO 对象。

export const ConvertToCreateCatDto = createRouteParamDecorator((data, req): CreateCatDto => { // `createParamDecorator` for nest old version
    if (req.body.tags.every(value => typeof value === "string")) { // if input tags is a string[]
        req.body.tags = (req.body.tags as string[]).map<TagDto>((tag) => {
            return { // convert to TagDto
                name: tag + ""
            }
        });
    }
    let result = new CreateCatDto(req.body);
    // TODO: validate `result` object
    return result;
});

add constructor to CreateCatDto将构造函数添加到 CreateCatDto

export class CreateCatDto {
    readonly title: string;
    readonly file: number;
    readonly tags: TagDto[];

    constructor(obj: any) {
        this.title = obj.title;
        this.file = obj.file;
        this.tags = obj.tags;
    }
}

Finally, use @ConvertToCreateCatDto instead of @Body in you controller.最后,在控制器中使用@ConvertToCreateCatDto而不是@Body

// In photo.controller.ts
@Post()
async create(@ConvertToCreateCatDto() createPhotoDto: PhotoDto): Promise<PhotoEntity> {
   //...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM