如何在使用 Typegoose 获取数据时使用 class-transformer 序列化嵌套 js 响应?

Posted

技术标签:

【中文标题】如何在使用 Typegoose 获取数据时使用 class-transformer 序列化嵌套 js 响应?【英文标题】:How to serialize a nest js response with class-transformer while getting data with Typegoose? 【发布时间】:2020-06-16 21:58:02 【问题描述】:

我一直在尝试使用 Typegoose 和 class-transformer 库来完成 Mongodb 的序列化部分的 NestJs 示例。 https://docs.nestjs.com/techniques/serialization 给出的示例仅显示了如何在 TypeORM 中使用序列化。我对 Typegoose 遵循了相同的过程。这是我到目前为止所尝试的。

// cat.domain.ts

import  prop  from '@typegoose/typegoose';

export class Cat 
  @prop()
  name: string;

  @prop()
  age: number;

  @prop()
  breed: string;



// cats.service.ts

@Injectable()
export class CatsService 
  constructor(
    @InjectModel(Cat) private readonly catModel: ReturnModelType<typeof Cat>,
  ) 

  findAll(): Observable<Cat[]> 
    return from(this.catModel.find().exec());
  

  findOne(id: string): Observable<Cat> 
    return from(this.catModel.findById(id).exec());
  
  ...


// cat.response.ts

import  ObjectId  from 'mongodb';
import  Exclude, Transform  from 'class-transformer';

export class CatResponse 
  @Transform(value => value.toString(),  toPlainOnly: true )
  _id?: ObjectId;

  name: string;

  age: number;

  @Exclude()
  breed: string;

  constructor(partial: Partial<CatResponse>) 
    Object.assign(this, partial);
  


// cats.controller.ts

@Controller('cats')
@UseInterceptors(ClassSerializerInterceptor)
export class CatsController 
  constructor(private readonly catsService: CatsService) 

  @Get()
  findAll(): Observable<CatResponse[]> 
    return this.catsService.findAll();
  

  @Get(':id')
  findOne(@Param() params: FindOneParamsDto): Observable<CatResponse> 
    return this.catsService.findOne(params.id);
  
  ...

我尝试使用 id 在 Get() 上运行 API 调用,但 breed 没有被排除在响应之外,而是得到了以下响应。


    "$__": 
        "strictMode": true,
        "selected": ,
        "getters": ,
        "_id": 
            "_bsontype": "ObjectID",
            "id": 
                "type": "Buffer",
                "data": [
                    94,
                    93,
                    76,
                    66,
                    116,
                    204,
                    248,
                    112,
                    147,
                    216,
                    167,
                    205
                ]
            
        ,
        "wasPopulated": false,
        "activePaths": 
            "paths": 
                "_id": "init",
                "name": "init",
                "age": "init",
                "breed": "init",
                "__v": "init"
            ,
            "states": 
                "ignore": ,
                "default": ,
                "init": 
                    "_id": true,
                    "name": true,
                    "age": true,
                    "breed": true,
                    "__v": true
                ,
                "modify": ,
                "require": 
            ,
            "stateNames": [
                "require",
                "modify",
                "init",
                "default",
                "ignore"
            ]
        ,
        "pathsToScopes": ,
        "cachedRequired": ,
        "$setCalled": [],
        "emitter": 
            "_events": ,
            "_eventsCount": 0,
            "_maxListeners": 0
        ,
        "$options": 
            "skipId": true,
            "isNew": false,
            "willInit": true
        
    ,
    "isNew": false,
    "_doc": 
        "_id": 
            "_bsontype": "ObjectID",
            "id": 
                "type": "Buffer",
                "data": [
                    94,
                    93,
                    76,
                    66,
                    116,
                    204,
                    248,
                    112,
                    147,
                    216,
                    167,
                    205
                ]
            
        ,
        "name": "Sylver",
        "age": 14,
        "breed": "Persian Cat",
        "__v": 0
    ,
    "$locals": ,
    "$op": null,
    "$init": true

谁能帮我正确地序列化响应?

【问题讨论】:

【参考方案1】:

更新: class-transformer 现在可以与 typegoose 一起正常工作,look here for the documentation on how to use it


this is an known issue (#108)、typegoose (& mongoose) 与 class-transformer/class-validator 不兼容 这是因为 typegoose 需要将类转换为模式,而 mongoose 会将其编译为模型(不再是类)

【讨论】:

你会如何建议我从我的回复中排除项目?我正在考虑做一些类似的事情,比如创建一个转换拦截器和一个自定义装饰器,并检查拦截器中的装饰器,但这似乎需要做很多工作,而且我还没有那么有经验。有更简单的方法吗? 当它是像“password”这样的值时,您可以将“select: false”设置为@prop 选项,默认情况下它不包含在任何查询中,除非再次明确选择它,否则转换它到POJO(通过.toJSON.lean)并过滤掉您不想要的任何内容(黑名单/白名单) 如何在使用 MONGOOSE 获取数据的同时使用 class-transformer 序列化嵌套 js 响应? 答案中的链接显示“404 Not found”。 @RollerCosta,我已经更新了链接【参考方案2】:

这里有一个解决方法:

// cats.controller.ts
...
import  classToPlain  from "class-transformer";
...

@Controller('cats')
@UseInterceptors(ClassSerializerInterceptor)
export class CatsController 
  constructor(private readonly catsService: CatsService) 

  @Get()
  findAll(): Observable<CatResponse[]> 
    const cats = this.catsService.findAll();
    // transforming the Model to CatResponse class...
    const catResponses = cats.map(cat => classToPlain(new CatResponse(cat.toJSON())))
    return catResponses;
  

  @Get(':id')
  findOne(@Param() params: FindOneParamsDto): Observable<CatResponse> 
    const cat = this.catsService.findOne(params.id);
    const catResponse = classToPlain(new CatResponse(cat.toJSON()));
    return 
  
  ...

希望对您有所帮助。

【讨论】:

以上是关于如何在使用 Typegoose 获取数据时使用 class-transformer 序列化嵌套 js 响应?的主要内容,如果未能解决你的问题,请参考以下文章

仅调用 Typegoose 进行保存操作时,钩子在 updateOne 上不起作用

我可以在 Typegoose 中使用私有属性吗?

Mongoose / typegoose 根据开始和结束索引获取数组

Mongodb,Typegoose - 通用存储库模式

Typegoose 和 NestJS:类型上不存在属性“保存”

Typescript typegoose getModelForClass 返回类型