[英]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:我用的是PhotoDto
我photo.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.