【问题标题】:NestJS - database entities is returned from the backend instead of DTONestJS - 从后端返回数据库实体而不是 DTO
【发布时间】:2020-12-28 22:37:21
【问题描述】:

我是 NestJs 的新手,我遇到了从后端返回响应实体的问题。

问题是即使我返回 Promise-PostDTO-.它仍然从数据库返回 Post 实体。用所有的属性和nestjs忽略返回类型。

你知道哪里有问题吗?我认为自动转换在两种方式下都有效。 也许问题在于返回 Promise 而不是 PostDTO。

我希望有人可以帮助我。 谢谢

下面的代码。

post.entity.ts

import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
} from 'typeorm';

@Entity()
export default class Post {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  title: string;

  @Column()
  perex: string;

  @Column()
  content: string;

  @CreateDateColumn()
  createdAt: Date;
}

post.dto.ts

import { IsString } from 'class-validator';
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class PostDTO {
  @Expose()
  @IsString()
  readonly title: string;
  @Expose()
  @IsString()
  readonly perex: string;
  @Expose()
  @IsString()
  readonly content: string;
}

post.repository.ts

import { Repository, EntityRepository } from 'typeorm';
import Post from './post.entity';
@EntityRepository(Post)
export class PostRepository extends Repository<Post> {}

post.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import Post from './post.entity';
import { PostRepository } from './post.repository';
import { PostDTO, PostRO } from './post.dto';

@Injectable()
export class PostService {
  constructor(
    @InjectRepository(Post) private readonly postRepository: PostRepository,
  ) {}
  async createPost(post: PostDTO): Promise<PostDTO> {
    return await this.postRepository.save(post);
  }
}

post.controller.ts

import {
  Controller,
  Get,
  Post,
  Body,
  UseInterceptors,
  ClassSerializerInterceptor,
} from '@nestjs/common';
import { PostService } from './post.service';
import { PostDTO } from './post.dto';
@Controller('post')
@UseInterceptors(ClassSerializerInterceptor)
export class PostController {
  constructor(private readonly postService: PostService) {}

  @Post()
  async createPost(@Body() post: PostDTO): Promise<PostDTO> {
    return await this.postService.createPost(post);
  }
}

根据 Jay McDoniel 的回答,我将代码重构为此。

我使用了转换类的自定义 nestjs 拦截器。

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { plainToClass } from 'class-transformer';

interface ClassType<T> {
  new (): T;
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<Partial<T>, T> {
  constructor(private readonly classType: ClassType<T>) {}

  intercept(
    context: ExecutionContext,
    call$: CallHandler<Partial<T>>,
  ): Observable<T> {
    return call$.handle().pipe(map(data => plainToClass(this.classType, data)));
  }
}

我还使用类转换器包重构了我的 DTO。

import { IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Exclude, Expose } from 'class-transformer';
@Exclude()
export class ArticleDTO {
  @Expose()
  @ApiProperty({ required: true })
  @IsString()
  readonly title: string;
  @Expose()
  @ApiProperty({ required: true })
  @IsString()
  readonly perex: string;
  @Expose()
  @ApiProperty({ required: true })
  @IsString()
  readonly content: string;
}

在我的控制器中使用新的迭代器。

import { Controller, Get, Post, Body, UseInterceptors } from '@nestjs/common';
import { ArticleService } from './article.service';
import { ArticleDTO } from './dto/article.dto';
import { TransformInterceptor } from 'src/transform.interceptor';
@Controller('articles')
@UseInterceptors(new TransformInterceptor(ArticleDTO))
export class ArticleController {
  constructor(private readonly articleService: ArticleService) {}
  @Get()
  async getAllPosts(): Promise<ArticleDTO[]> {
    return await this.articleService.getAllPosts();
  }

  @Post()
  async createPost(@Body() post: ArticleDTO): Promise<ArticleDTO> {
    return await this.articleService.createPost(post);
  }
}

这些代码更新后,它按预期工作,因此它返回 DTO 而不是 DB 实体。

【问题讨论】:

    标签: javascript entity nestjs dto


    【解决方案1】:

    Typescript 是一个非常有用的开发工具,但有时会出现问题。在这种情况下,您告诉 Typescript PostController#createPost 将返回 PostDTO,如上所述,它具有 titleperexcontent 的属性。现在,当您对this.postRepository.save() 运行查询时,该方法返回一个Post 对象,该对象具有idtitle,perexcontentcreatedAt。因为Post 对象与PostDTO 具有相同的字段(以及更多字段),所以Typescript 不会告诉您您没有返回相同的对象。同样,这些类型在运行时会消失,因为 Typescript 是一种开发工具,而不是运行时(除非你使用的是 Deno),所以 JavaScript 看到的只是 return await this.postRepository.save(),这就是将被发送回调用者的内容。

    如何解决这个问题

    1. 使用局部变量并删除 idcreatedAt 字段
    2. 使用class-transformerMarshalTS 之类的序列化程序将结果转换为所需的对象。

    【讨论】:

      猜你喜欢
      • 2016-11-08
      • 2022-01-12
      • 2021-04-20
      • 2020-07-23
      • 1970-01-01
      • 1970-01-01
      • 2019-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多