繁体   English   中英

如何使用 NestJS 正确设置序列化?

[英]How to properly set up serialization with NestJS?

我开始在一个新的 NestJs 项目中工作,但是当我尝试实现序列化时遇到了一个问题。 我想在对象被发送到网络响应之前实现序列化来转换对象。 我的项目工作正常,但是当我尝试在控制器中实现ClassSerializerInterceptor 时,出现以下错误:

 [Nest] 27010 - 12/23/2019, 8:20:53 PM [ExceptionsHandler] Maximum call stack size exceeded +29851ms RangeError: Maximum call stack size exceeded at Object.Console.<computed> (internal/console/constructor.js:241:9) at Object.log (internal/console/constructor.js:282:26) at Object.consoleCall (<anonymous>) at _loop_1 (/path/to/my/project/node_modules/class-transformer/TransformOperationExecutor.js:146:47)

我更改了ClassSerializerInterceptor的范围来解决问题,但错误仍然存​​在。 根据文档,我需要在控制器中使用拦截器并在实体中使用相应的装饰器来实现序列化。 我的序列化实现如下:

billing-statement.controller.ts

 import { ClassSerializerInterceptor, Controller, Get, Query, UseInterceptors } from '@nestjs/common'; import { BillingStatementService } from './billing-statement.service'; import { BillingStatementDto } from './billing-statement.dto'; import { BillingStatement } from './billing-statement.entity'; @Controller('billing-statement') export class BillingStatementController { constructor(private readonly billingStatementService: BillingStatementService) {} @Get() @UseInterceptors(ClassSerializerInterceptor) async getBillingStatement( @Query() query: BillingStatementDto, ): Promise<BillingStatement> { return this.billingStatementService.findBillingStatementByUser(+query.id); } }

billing-statement.entity.ts

 import { AutoIncrement, BelongsTo, Column, ForeignKey, HasMany, Model, PrimaryKey, Table } from 'sequelize-typescript'; import { User } from '../users/user.entity'; import { Payment } from './payment.entity'; import { Exclude } from 'class-transformer'; @Table({ tableName: 'billing_statement_tbl', timestamps: false, }) export class BillingStatement extends Model<BillingStatement> { @AutoIncrement @PrimaryKey @Column({field: 'billing_statement_id_pk'}) id: number; @Column currency: string; @Column({field: 'total_amount'}) totalAmount: number; @Exclude() @Column({field: 'contract_start'}) contractStart: Date; @Exclude() @Column({field: 'contract_end'}) contractEnd: Date; @HasMany(() => Payment) payments: Payment[]; }

我不知道我做错了什么或错误的根源是什么。

从我目前看到的情况来看,我想到了两件事。

  1. 扩展class-transformer class-validator使用,并在实体类中使用class-validator ,以排除整个类的属性,并仅在生成的序列化对象中公开所需的属性。

代码是这样的:

billing-statement.entity.ts

import { AutoIncrement, BelongsTo, Column, ForeignKey, HasMany, Model, PrimaryKey, Table } from 'sequelize-typescript';
import { User } from '../users/user.entity';
import { Payment } from './payment.entity';
import { Exclude, Expose, Type } from 'class-transformer';
import { IsArray, IsNumber, IsString } from 'class-validator';

@Exclude()
@Table({
  tableName: 'billing_statement_tbl',
  timestamps: false,
})
export class BillingStatement extends Model<BillingStatement> {
  @AutoIncrement
  @PrimaryKey
  @Column({field: 'billing_statement_id_pk'})
  @Expose()
  @IsNumber()
  id: number;

  @Column
  @Expose()
  @IsString()
  currency: string;

  @Column({field: 'total_amount'})
  @Expose()
  @IsNumber()
  totalAmount: number;

  @Column({field: 'contract_start'})
  contractStart: Date;

  @Column({field: 'contract_end'})
  contractEnd: Date;

  @HasMany(() => Payment)
  @IsArray()
  @Expose()
  @Type(() => Payment)
  payments: Payment[];
}
  1. 另一种方法是将您的实体定义与返回的 dto 定义分开,这样您就可以扩展实体的定义,加上或减去返回的 dto 中想要和不需要的属性。 例如,假设您将响应命名为 dto BillingStatementResponseDto ,您将在控制器响应类型中使用这个。 BillingStatementResponseDto可以包含外部 api 对象的属性(例如从一些外部 api 获取,一些实体属性和一些传入请求 dto 的属性。您还可以扩展class-transformer class-validator使用并使用class-validatorBillingStatementResponseDto定义中的第一条建议。

代码如下所示:

billing-statement.entity.ts (保持不变,没有类转换器的东西)

import { AutoIncrement, BelongsTo, Column, ForeignKey, HasMany, Model, PrimaryKey, Table } from 'sequelize-typescript';
import { User } from '../users/user.entity';
import { Payment } from './payment.entity';

@Table({
  tableName: 'billing_statement_tbl',
  timestamps: false,
})
export class BillingStatement extends Model<BillingStatement> {
  @AutoIncrement
  @PrimaryKey
  @Column({field: 'billing_statement_id_pk'})
  id: number;

  @Column
  currency: string;

  @Column({field: 'total_amount'})
  totalAmount: number;

  @Column({field: 'contract_start'})
  contractStart: Date;

  @Column({field: 'contract_end'})
  contractEnd: Date;

  @HasMany(() => Payment)
  payments: Payment[];
}

billing-statement-response.dto.ts (目标返回对象的新文件定义,使用class-transformerclass-validator ) - 导入并在您的控制器中使用

import { Exclude, Expose, Type } from 'class-transformer';
import { IsArray, IsNumber, IsString, ValidateNested } from 'class-validator';

@Exclude()
export class BillingStatementResponseDto {
  @Expose()
  @IsNumber()
  id: number;

  @Expose()
  @IsString()
  currency: string;

  @Expose()
  @IsNumber()
  totalAmount: number;

  @IsArray()
  @ValidateNested()
  @Expose()
  @Type(() => Payment)
  payments: Payment[];
}

billing-statement.controller.ts

import { ClassSerializerInterceptor, Controller, Get, Query, UseInterceptors } from '@nestjs/common';
import { BillingStatementService } from './billing-statement.service';
import { BillingStatementDto } from './billing-statement.dto';
import { BillingStatementResponseDto } from './billing-statement-response.dto'; // <= import your newly defined dto 

@Controller('billing-statement')
export class BillingStatementController {
  constructor(private readonly billingStatementService: BillingStatementService) {}

  @Get()
  @UseInterceptors(ClassSerializerInterceptor)
  async getBillingStatement(
    @Query() query: BillingStatementDto,
  ): Promise<BillingStatementResponseDto> { // <= here you go for the use of BillingStatementResponseDto
    return this.billingStatementService.findBillingStatementByUser(+query.id);
  }
}

恕我直言,第二个解决方案在层分离、灵活性、模块化和可维护性方面会更好:)

如果有帮助,请告诉我;)

根据错误消息,我认为您有循环引用问题。 只需注释掉 billing_statement 对象所指的其他对象,然后再试一次。 如果这就是您收到此错误的原因,您应该删除从子对象到父对象的引用,或者尽量不要序列化这些引用。

祝你好运。

暂无
暂无

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

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