如何在控制器中让指定路由跳过顶层@UseGuards装饰器?
NestJS 控制器路由跳过类级别守卫的解决方案
问题场景
现有如下控制器代码,类级别通过@UseGuards绑定了Guard1和Guard2,需要让GET /接口跳过这些守卫,且未使用全局守卫注册方式:
@ApiBearerAuth() @UseGuards(Guard1, Guard2) export class NotesController { @Post() async create() { //........ } @Get() async all() { //........ } }
此前尝试用SetMetadata标记路由跳过守卫,但因元数据读取方式错误导致方案失效。
可行解决方案
1. 创建自定义跳过守卫装饰器
定义一个装饰器,给目标路由添加“跳过守卫”的元数据标记:
import { SetMetadata } from '@nestjs/common'; // 定义元数据唯一标识键 export const SKIP_GUARDS = 'skipGuards'; // 自定义装饰器,用于标记路由跳过守卫 export const SkipGuards = () => SetMetadata(SKIP_GUARDS, true);
2. 修改守卫逻辑,检查元数据
给每个需要支持跳过逻辑的守卫注入Reflector,在canActivate方法中先检查目标路由是否有SKIP_GUARDS标记,有则直接放行:
以Guard1为例:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { SKIP_GUARDS } from './skip-guards.decorator'; @Injectable() export class Guard1 implements CanActivate { // 注入Reflector用于读取元数据 constructor(private readonly reflector: Reflector) {} canActivate(context: ExecutionContext): boolean | Promise<boolean> { // 优先读取路由(handler)的元数据,再读取类的元数据 const needSkip = this.reflector.getAllAndOverride<boolean>(SKIP_GUARDS, [ context.getHandler(), context.getClass(), ]); if (needSkip) { return true; // 跳过当前守卫逻辑 } // 此处保留原守卫的校验逻辑 // ... return true; // 替换为实际的校验结果 } }
Guard2需要做完全相同的修改,确保两个守卫都会检查该元数据。
3. 在目标路由上应用装饰器
在需要跳过守卫的GET路由上添加@SkipGuards()装饰器:
@ApiBearerAuth() @UseGuards(Guard1, Guard2) export class NotesController { @Post() async create() { //........ } @Get() @SkipGuards() // 标记该路由跳过类级别守卫 async all() { //........ } }
说明
NestJS中SetMetadata会在守卫执行前完成元数据注入,此前的问题是未正确使用Reflector读取路由级别的元数据。通过getAllAndOverride方法,我们可以优先获取路由上的元数据,确保路由标记能覆盖类级别的守卫配置。
内容的提问来源于stack exchange,提问作者Mehul Chaturvedi
相关产品推荐
相关产品推荐

