nestjs 不使用装饰器获取用户信息

209 阅读2分钟

在以往的代码中,通常使用装饰器来获取用户信息. 先在guard挂载用户信息到request上, 然后写一个装饰器获取到request上的用户信息, 再通过controller获取用户信息, 然后传到service 里面。

guard.ts

const payload = await this.authService.getTokenValue(token);
request['user'] = payload;

用户装饰器

export const WebUser = createParamDecorator(
    (data: { required: boolean } = {
        required: true
    }, ctx: ExecutionContext) => {
        const { required } = data;
        const request = ctx.switchToHttp().getRequest<Request>();
        const user = (request.user as IWebTokenValue);
        if (required && !user) {
            throw new BadRequestException('用户不存在');
        }
        return user;
    }
);

controller

@ApiOperation({ summary: '给帖子投币' })
@Put('coin/:postId')
giveCoinToPost(@Param() data: PostCoinGiveInteractionDto, @WebUser user: User) {
    return this.postUserInteractionService.giveCoinToPost(data, user);
}

service 层还需要接收user对象。

这样的话, 每个接口获取用户信息都需要声明好几个user参数。

是否有一种办法在service层直接获取request对象?

asyncLocalStorage

AsyncLocalStorage 是基于 async_hooks API 的 Node.js API,它提供了一种在应用程序中传播本地状态的替代方式,而无需将其显式地作为函数参数传递。它类似于其他编程语言中的线程局部存储。 Async Local Storage 的主要思想是,我们可以使用 AsyncLocalStorage#run 调用来“包装”某个函数调用。在包装调用内部调用的所有代码都可以访问相同的 store,该 store 对于每个调用链都是唯一的。 在 NestJS 的上下文中,这意味着如果我们可以在请求的生命周期内找到一个适当的位置,在这个位置将剩余的请求代码进行包装,我们将能够访问和修改仅对该请求可见的状态。这可能作为替代 REQUEST 作用域提供者及其一些限制的方法。 或者,我们可以使用 ALS 仅在系统的一部分中传播上下文(例如“事务”对象),而无需在各个服务之间显式传递它,这可以增加隔离性和封装性。 Async Local Storage | NestJS - 一个渐进式的Node.js框架

由于已经安装了 nestjs-cls 这个库用来记录请求ID,这里直接使用nestjs-cls 记录用户信息。

在 guard 里面获取到用户信息后,我们就可以将用户信息挂载到cls 上。

guard.ts

import { ClsService } from 'nestjs-cls';

const payload = await this.authService.getTokenValue(token);
request['user'] = payload;
this.clsService.set('user', payload);

接下来就简单了,直接在业务的service里面引入clsService, 然后读取 user 就可以了。

async getSelfVideoList(query: SelfVideoPostQuery) {
        const { userId } = this.appClsService.getUser<IWebTokenValue>();
   }

上面的 appClsService 只是 clsService 的一层封装, 用来获取ts的类型的。