在NestJS路由中,我想根据请求(用户角色)序列化响应。这意味着我需要将选项
groups: []
传递到 transformToPlain
中的 ClassSerializerInterceptor
方法中,以便类转换器可以正确返回过滤格式:
我刚刚扩展了此类并更改了
intercept
方法,以根据请求包含 group
选项:
export class CustomClassSerializerInterceptor extends ClassSerializerInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// @ts-ignore
const contextOptions = this.getContextOptions(context);
const options = {
...this.defaultOptions,
...contextOptions,
groups: request.user.roles // Pseudo
};
return next
.handle()
.pipe(map((res: PlainLiteralObject | Array<PlainLiteralObject>) => this.serialize(res, options)));
}
}
我的实体:
export class Content extends BaseDatabaseEntity {
@Column()
@Transform((type) => EContentType[type])
type: EContentType;
@Expose({ groups: ["MODERATOR", "ADMIN"] })
@Column({ default: "[]", type: "json" })
data: TContentDataColumn[];
}
这是正确的做法吗?例如
this.getContextOptions
方法在原始源中是私有的,所以我需要在这里执行 ts-ignore ,以覆盖类的默认预期隐私,这在我看来是很大的禁忌。
我是否应该根据用户的角色来转换 API 响应,或者这是反模式?
您可以使用
@SerializationOptions()
装饰器根据触发的路由来传递额外选项。由于您似乎需要动态执行此操作,因此您甚至可以使用 Reflect
命名空间并设置每个请求的元数据。元数据标记是 'class_serializer:options'
,所以你可以做类似的事情
Reflect.defineMetadata(
'class_serializer:options',
{ groups: req.user.roles },
Class.prototype,
'method'
);
不一定很漂亮,但可以用
是的,扩展
ClassSerializerInterceptor
是向其中添加更多功能的充分方法。
正如@Baterka在他的问题中指出的那样,这总是有效的,但会触发编译器警告。
从那时起,源代码已更改,因此以前的 private 字段(如
defaultOptions
和 getContextOptions
)现在改为 protected,这意味着它们可用于派生类。
这是一个与 NestJS 8.0 或更高版本完全兼容的工作示例:
角色序列化器.interceptor.ts
import { CallHandler, ClassSerializerInterceptor, ExecutionContext, Injectable, PlainLiteralObject } from '@nestjs/common'; import { map, Observable } from 'rxjs'; @Injectable() export class RolesSerializerInterceptor extends ClassSerializerInterceptor { intercept( context: ExecutionContext, next: CallHandler ): Observable<any> { const userRoles = context.switchToHttp().getRequest().user?.roles ?? []; const contextOptions = this.getContextOptions(context); const options = { ...this.defaultOptions, ...contextOptions, groups: userRoles }; return next .handle() .pipe( map((res: PlainLiteralObject | Array<PlainLiteralObject>) => this.serialize(res, options) ) ); } }```