【问题标题】:class serialization not working in nestjs类序列化在nestjs中不起作用
【发布时间】:2020-04-30 06:26:02
【问题描述】:

我有一个简单的用户模型,我想从中排除密码。使用official docsanswer here 我试图让它工作,但这似乎不起作用,因为我得到了这样的回应。

[
  {
    "$__": {
      "strictMode": true,
      "selected": {},
      "getters": {},
      "_id": {
        "_bsontype": "ObjectID",
        "id": {
          "type": "Buffer",
          "data": [
            94,
            19,
            73,
            179,
            3,
            138,
            216,
            246,
            182,
            234,
            62,
            37
          ]
        }
      },
      "wasPopulated": false,
      "activePaths": {
        "paths": {
          "password": "init",
          "email": "init",
          "name": "init",
          "_id": "init",
          "__v": "init"
        },
        "states": {
          "ignore": {},
          "default": {},
          "init": {
            "_id": true,
            "name": true,
            "email": true,
            "password": true,
            "__v": true
          },
          "modify": {},
          "require": {}
        },
        "stateNames": [
          "require",
          "modify",
          "init",
          "default",
          "ignore"
        ]
      },
      "pathsToScopes": {},
      "cachedRequired": {},
      "session": null,
      "$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,
            19,
            73,
            179,
            3,
            138,
            216,
            246,
            182,
            234,
            62,
            37
          ]
        }
      },
      "name": "Kamran",
      "email": "kamran@example.com",
      "password": "Pass1234",
      "__v": 0
    },
    "$locals": {},
    "$init": true
  }
]

这是我的模型。我正在使用Typegoose,但Mongoose 也是如此。

export class User extends Typegoose {
  @Transform((value) => value.toString(), { toPlainOnly: true })
  _id: string;

  @prop({ required: true })
  public name!: string;

  @prop({ required: true })
  public email!: string;

  @Exclude({ toPlainOnly: true })
  @prop({ required: true })
  public password!: string;
}

我的用户服务

@Injectable()
export class UserService {
  constructor(@InjectModel(User) private readonly user: ReturnModelType<typeof User>) {}

  async getUsers() {
    return this.user.find().exec();
  }
}

和用户控制器

@Controller('users')
@UseInterceptors(ClassSerializerInterceptor)
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  async index() : Promise<User[] | []> {
    return this.userService.getUsers();
  }
}

我尝试按照here 的描述使用我的自定义拦截器,但这不起作用,所以我将其更改为下面的代码,如给定的here

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map(data => classToPlain(this.transform(data))));
  }

  transform(data) {
    const transformObject = (obj) => {
      const result = obj.toObject();
      const classProto = Object.getPrototypeOf(new User());
      Object.setPrototypeOf(result, classProto);
      return result;
    }

    return Array.isArray(data) ? data.map(obj => transformObject(obj)) : transformObject(data);
  }
}

现在它可以工作了,但代码不是通用的。有什么办法让它通用吗?

【问题讨论】:

  • 我遇到了完全相同的问题。你找到解决办法了吗?
  • @JPYamamoto 我刚刚发布了我的解决方案。试一试

标签: serialization nestjs class-transformer typegoose


【解决方案1】:

@kamran-arshad 的回答帮助我找到了使用 typegoose 实现预期结果的适当方法。您可以使用装饰器 @modelOptions() 并将其传递给带有函数的对象以生成 JSON。

@modelOptions({
  toJSON: {
    transform: function(doc, ret, options) {
      delete ret.password;
      return ret;
    }
  }
})
export class User extends Typegoose {
@prop({required: true})
name!: string;

@prop({required: true})
password!: string;
}

它并不完美,因为来自class-transform 的装饰器不能按预期工作,但它完成了工作。此外,您应该避免使用ClassSerializerInterceptor,因为它会给出与 OP 提到的相同的结果。

【讨论】:

    【解决方案2】:

    我想我已经发现了问题,但还不确定为什么会发生这种情况。因此,如果我返回类的实例,那么序列化工作就会出现问题,但是如果我只返回普通的 db 响应,那么就会出现上述问题。所以我所做的是将toObjecttransform 方法中的响应对象的原型更新为我的用户类。这是代码。

    用户模型

    @modelOptions({
      schemaOptions: {
        toObject: {
          transform: function(doc, ret, options) {
            Object.setPrototypeOf(ret, Object.getPrototypeOf(new User()));
          }
        },
      },
    })
    export class User {
      @Transform((value) => value.toString(), { toPlainOnly: true })
      public _id: string;
    
      @prop({ required: true })
      public name!: string;
    
      @prop({ required: true })
      public email!: string;
    
      @Exclude({ toPlainOnly: true })
      @prop({ required: true })
      public password!: string;
    }
    

    转换拦截器

    @Injectable()
    export class TransformInterceptor implements NestInterceptor {
      intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        return next.handle().pipe(map(data => classToPlain(this.transform(data))));
      }
    
      transform(data) {
        return Array.isArray(data) ? data.map(obj => obj.toObject()) : data.toObject();
      }
    }
    

    现在,如果您只是用@UseInterceptors(TransformInterceptor) 装饰您的控制器或方法,它将完美地工作。这是一个typegoose 解决方案,但它也适用于mongoose

    【讨论】:

    • 你知道 Typegoose 是否可以做到这一点?
    • @JPYamamoto 我已经使用通用解决方案将我的答案更新为typegoose
    【解决方案3】:

    为了避免 Mongoose 出现任何背痛和头痛, 我建议使用 plainToClass 来获得完整的 mongoose/class-transform 兼容性,并避免必须进行自定义覆盖来克服这个问题。

    例如,将其添加到您的服务中:

    async validateUser(email: string, password: string): Promise<UserWithoutPassword | null> {
        const user = await this.usersService.findOne({ email });
    
        if (user && await compare(password, user.password))
        {
            return plainToClass(UserWithoutPassword, user.toObject());
        }
    
        return null;
    }
    
    

    这样你就可以使用@Exclude()和其他装饰器了

    来源:Stackoverflow answer

    【讨论】:

      【解决方案4】:

      这是我的实现,所有装饰器都可以工作,不需要ClassSerializerInterceptor

      PersonSchema.methods.toJSON = function () {
        return plainToClass(Person, this.toObject());
      };
      

      【讨论】:

        猜你喜欢
        • 2019-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-16
        相关资源
        最近更新 更多