2022-11-13 Nest.js 中关于守卫的使用
参考:
Nest.js:https://docs.nestjs.cn/
守卫:https://docs.nestjs.cn/9/guards
基本使用
在 Nest.js 中,守卫是一个使用 @Injectable() 装饰器的类,守卫应该实现 CanActivate 接口。
一个简单的例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Observable } from 'rxjs';
  @Injectable() export class AuthGuard implements CanActivate {   canActivate(     context: ExecutionContext,   ): boolean | Promise<boolean> | Observable<boolean> {     const request = context.switchToHttp().getRequest();     return validateRequest(request);    } }
 
 
  | 
 
深入使用
自定义元数据
在守卫的执行过程中,难免会需要根据不同的情况做不同的处理,此时就需要用到自定义元数据。
在 Nest.js 中,自定义元数据是非常方便的,可以使用  @SetMetadata() 装饰器将定制元数据。
1 2 3 4 5 6 7
   |  @Post() @SetMetadata('roles', ['admin'])  async create(@Body() createCatDto: CreateCatDto) {   this.catsService.create(createCatDto); }
 
 
  | 
 
很显然的是,直接调用 @SetMetadata() 并不是一个很好的做法,更好的写法是创建自己的装饰器,例如:
1 2 3 4
   |  import { SetMetadata } from '@nestjs/common';
  export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
 
  | 
 
之后只需要在要用到的地方调用即可。
1 2 3 4 5 6
   |  @Post() @Roles('admin') async create(@Body() createCatDto: CreateCatDto) {   this.catsService.create(createCatDto); }
 
  | 
 
使用元数据
在自定义了元数据之后,我们还需要拿到元数据的值才能使用,在这里可以使用 @nestjs/core 中提供的 Reflector 帮助类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   |  import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core';
  @Injectable() export class RolesGuard implements CanActivate {   constructor(private reflector: Reflector) {}
    canActivate(context: ExecutionContext): boolean {     const roles = this.reflector.get<string[]>('roles', context.getHandler());      if (!roles) {       return true;     }     const request = context.switchToHttp().getRequest();     const user = request.user;     return matchRoles(roles, user.roles);   } }
 
 
  | 
 
元数据扩展
更多相关内容可参考:应用上下文
在上一节中,我们通过 context.getHandler() 来获取元数据,但这里获取的元数据只能是 方法 的,无法获取到 类  的,那么要怎么获取到 类 的元数据呢?
答案是通过 context.getClass(),即:
1
   | const roles = this.reflector.get<string[]>('roles', context.getClass()); 
  | 
 
那么接下来问题来了,如果要先从 类 获取元数据,再从 方法 获取元数据,要怎么办呢?
Nest.js 也提供了相应的方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   |  @Roles('user') @Controller('cats') export class CatsController {   @Post()   @Roles('admin')   async create(@Body() createCatDto: CreateCatDto) {     this.catsService.create(createCatDto);   } }
 
 
  const roles = this.reflector.getAllAndOverride<string[]>('roles', [   context.getHandler(),   context.getClass(), ]);
 
  const roles = this.reflector.getAllAndOverride<string[]>('roles', [   context.getHandler(),   context.getClass(), ]);
 
 
  | 
 
总结
通过  @SetMetadata()  设置元数据,再通过 Reflector 获取元数据,我们就可以非常方便的在守卫、拦截器、管道或者其他 Nest.js 组件中使用元数据,极大的简化了装饰器的使用。
本文作者:草梅友仁
本文地址: https://blog.cmyr.ltd/archives/b26331ff.html 
版权声明:本文采用 CC BY-NC-SA 4.0 协议 进行分发,转载请注明出处!