序列化
序列化
序列化是在对象在网络响应中返回之前发生的过程。这是提供转换和清理要返回给客户端的数据规则的适当位置。例如,像密码这样的敏感数据应该始终从响应中排除。或者,某些属性可能需要额外的转换,例如只发送实体属性的子集。手动执行这些转换可能很繁琐且容易出错,并且可能让您不确定是否已涵盖所有情况。
概述
Nest 提供了一个内置功能,帮助确保这些操作可以以直接的方式执行。ClassSerializerInterceptor 拦截器使用强大的 class-transformer 包来提供声明式和可扩展的对象转换方式。它执行的基本操作是获取方法处理程序返回的值,并应用来自 class-transformer 的 instanceToPlain() 函数。在这样做时,它可以应用在实体/DTO 类上通过 class-transformer 装饰器表达的规则,如下所述。
提示 序列化不适用于 StreamableFile 响应。
排除属性
假设我们想要自动从用户实体中排除 password 属性。我们按如下方式注释实体:
import { Exclude } from 'class-transformer';
export class UserEntity {
id: number;
firstName: string;
lastName: string;
@Exclude()
password: string;
constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial);
}
}
现在考虑一个控制器,它有一个返回此类实例的方法处理程序。
@UseInterceptors(ClassSerializerInterceptor)
@Get()
findOne(): UserEntity {
return new UserEntity({
id: 1,
firstName: 'John',
lastName: 'Doe',
password: 'password',
});
}
警告 请注意,我们必须返回类的实例。如果您返回一个普通的 JavaScript 对象,例如 {{ '{' }} user: new UserEntity() {{ '}' }},该对象将不会被正确序列化。
提示 ClassSerializerInterceptor 从 @nestjs/common 导入。
当请求此端点时,客户端收到以下响应:
{
"id": 1,
"firstName": "John",
"lastName": "Doe"
}
请注意,拦截器可以应用于整个应用程序(如这里所述)。拦截器和实体类声明的组合确保返回 UserEntity 的任何方法都将确保删除 password 属性。这为您提供了对此业务规则的集中执行措施。
暴露属性
您可以使用 @Expose() 装饰器为属性提供别名,或执行函数来计算属性值(类似于 getter 函数),如下所示。
@Expose()
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
转换
您可以使用 @Transform() 装饰器执行额外的数据转换。例如,以下构造返回 RoleEntity 的 name 属性,而不是返回整个对象。
@Transform(({ value }) => value.name)
role: RoleEntity;
传递选项
您可能希望修改转换函数的默认行为。要覆盖默认设置,请在 @SerializeOptions() 装饰器的 options 对象中传递它们。
@SerializeOptions({
excludePrefixes: ['_'],
})
@Get()
findOne(): UserEntity {
return new UserEntity();
}
提示 @SerializeOptions() 装饰器从 @nestjs/common 导入。
通过 @SerializeOptions() 传递的选项作为底层 instanceToPlain() 函数的第二个参数传递。在此示例中,我们自动排除所有以 _ 前缀开头的属性。
转换普通对象
您可以通过使用 @SerializeOptions 装饰器在控制器级别强制执行转换。这确保所有响应都转换为指定类的实例,应用来自 class-validator 或 class-transformer 的任何装饰器,即使返回普通对象也是如此。这种方法导致更清洁的代码,无需重复实例化类或调用 plainToInstance。
在下面的示例中,尽管在两个条件分支中都返回普通 JavaScript 对象,但它们将自动转换为 UserEntity 实例,并应用相关装饰器:
@UseInterceptors(ClassSerializerInterceptor)
@SerializeOptions({ type: UserEntity })
@Get()
findOne(@Query() { id }: { id: number }): UserEntity {
if (id === 1) {
return {
id: 1,
firstName: 'John',
lastName: 'Doe',
password: 'password',
};
}
return {
id: 2,
firstName: 'Kamil',
lastName: 'Mysliwiec',
password: 'password2',
};
}
提示 通过为控制器指定预期的返回类型,您可以利用 TypeScript 的类型检查功能来确保返回的普通对象符合 DTO 或实体的形状。plainToInstance 函数不提供这种级别的类型提示,如果普通对象与预期的 DTO 或实体结构不匹配,可能会导致潜在的错误。
示例
可以在这里找到一个工作示例。
WebSockets 和微服务
虽然本章显示了使用 HTTP 风格应用程序(例如 Express 或 Fastify)的示例,但 ClassSerializerInterceptor 对 WebSockets 和微服务的工作方式相同,无论使用何种传输方法。
了解更多
阅读更多关于 class-transformer 包提供的可用装饰器和选项的信息,请访问这里。