【问题标题】:How to get req.user in services?如何在服务中获取 req.user?
【发布时间】:2021-11-17 20:46:45
【问题描述】:

我在 NestJs 中创建了一个身份验证中间件,如下所示:

@Injectable()
export class AuthenticationMiddleware implements NestMiddleware {
    constructor() {}

    async use(req: any, res: any, next: () => void) {        
        const authHeaders = req.headers.authorization;
        
        if (authHeaders) {

            //some logic etc.
            //req.user = user;

            next();
        } else {
            throw new UnathorizedException();
        }
    }
}

...我从标头中获取 - 一个身份验证令牌,对其进行解码并检查该用户是否正确并存在于数据库中,如果他存在,那么我将 user 对象设置为 req.user。现在我有一个问题,如何在我的服务中获取这个req.user 并在业务逻辑中使用?我需要从req.user 获取 id,但我不知道如何。

我知道我可以通过在控制器参数中使用@Req() request 来做到这一点,并将这个request 传递到我的函数中,但我不想要它,因为(对我来说)这是一种丑陋的做法。那么,如何让这个req.user 进入我的服务?

感谢您的帮助!

【问题讨论】:

  • 好问题。这就是使用装饰器的原因。看看我的回答。

标签: javascript node.js typescript request nestjs


【解决方案1】:

你可以像这样定义自己的请求接口

import { Request } from 'express';
...

export interface IRequestWithUser extends Request {
  user: User;
}

然后只需将req 参数的类型赋予IRequestWithUser

【讨论】:

  • 但是如何在服务方法中使用这个接口呢?
  • 你可以给req参数的类型,或者你甚至可以覆盖来自express lib的类型Request。
【解决方案2】:

好吧,要让用户使用服务,您有两种选择:

  1. 在控制器中使用 @Req() 并传递它,正如你所提到的

  2. 使您的服务REQUEST scoped 并将请求对象注入服务

就我个人而言,我会选择前者,因为请求范围界定在开始权衡和处理方面有其自身的利弊(例如无法在护照策略或 cron 作业中使用该服务)。您也可以只将用户设为可选,或将其捆绑到主体中或传递给服务的任何内容中,然后在不作为显式参数的情况下访问它。

【讨论】:

    【解决方案3】:

    你可以创建一个装饰器来完成它。像这样的

    current-user.decorator.ts

    import { createParamDecorator, ExecutionContext } from '@nestjs/common';
    
    export const CurrentUser = createParamDecorator(
      (property: string, ectx: ExecutionContext) => {
        const ctx = ectx.getArgByIndex(1);
        return property ? ctx.req.user && ctx.req.user[property] : ctx.req.user;
      },
    );
    

    example.controller.ts

    @ApiTags('example')
    @Controller('example')
    export class ExampleController {
        constructor(private readonly exampleService: ExampleService) {}
    
        @Get('/')
        public async doSomething(@CurrentUser() user: YourUserClassOrInteface,): Promise<any> {
            return this.exampleService.exampleFunction(user.id);
        }
    }
    

    example.service.ts

    
    export class ExampleService {
        constructor() {}
    
        public async exampleFunction(id: string): Promise<void> {
            console.log('id:', id);
            return;
        }
    }
    
    

    重要提示:在服务中注入请求不是一个好的解决方案,因为它会在每个端点请求中创建一个新请求。这就是使用装饰器的原因。它将使处理所需数据变得容易,并且不会仅移交所需的参数,而不是传输额外的大请求对象。

    【讨论】:

    • 如果我想使用装饰器,那么我将在控制器方法中使用 @Request 之类的东西,我写的是我不想使用它
    • 你不需要在控制器中使用@Request。我提供了一个装饰器、控制器和服务的示例。你能看到控制器中的@Request 吗?
    【解决方案4】:

    替代解决方案(如果您不使用请求范围注入):您可以使用async hooks。有许多库可以简化异步钩子的使用,例如 this one。您只需在中间件中设置上下文:

    @Injectable()
    export class AuthenticationMiddleware implements NestMiddleware {
        constructor() {}
    
        async use(req: any, res: any, next: () => void) {        
            const authHeaders = req.headers.authorization;
            
            if (authHeaders) {
    
                //some logic etc.
                //req.user = user;
    
                Context.run(next, { user: req.user });
            } else {
                throw new UnathorizedException();
            }
        }
    }
    
    

    然后您只需调用Context.get()即可在代码中的任何位置获取用户实例

    【讨论】:

      猜你喜欢
      • 2019-07-25
      • 2020-11-23
      • 2012-06-26
      • 2021-08-16
      • 1970-01-01
      • 2015-11-19
      • 2020-03-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多