Nest中GraphQL自定义指令抛出异常未被useGlobalFilters捕获
问题原因
Nest的全局异常过滤器仅拦截Nest执行链路内抛出的异常,包括REST接口、GraphQL Resolver、Nest守卫/拦截器/管道内的异常。你自定义的SchemaDirectiveVisitor是Apollo GraphQL原生的schema扩展能力,重写的字段resolve方法执行在GraphQL自身的解析执行链路中,抛出的异常会被GraphQL执行层直接捕获处理,不会进入Nest的异常拦截链路,所以全局过滤器无法捕获。
解决方案
你可以根据需求选择以下两种方案处理:
方案1:保留现有指令,通过GraphQL的formatError配置处理异常
在Nest的GraphQLModule注册时添加formatError配置,所有GraphQL执行链路的错误都会经过该方法,你可以在此处完成日志记录等逻辑,也可以直接复用现有AllExceptionsFilter里的处理逻辑:
// app.module.ts 中GraphQLModule注册配置 GraphQLModule.forRoot({ // 其他原有配置保持不变 formatError: (err) => { // 拿到原始抛出的错误对象 const originalError = err.originalError; if (originalError) { // 调用你原有AllExceptionFilter里的日志/处理逻辑即可 yourExceptionHandleMethod(originalError); } // 可自定义修改返回给前端的错误格式 return err; } })
方案2:替换自定义指令为Nest Guard,让异常进入Nest链路
Nest的GraphQL守卫执行在Nest的请求处理链路中,守卫内抛出的异常可以被全局异常过滤器正常捕获,这种方式更符合Nest的最佳实践,也能完全复用你已有的全局异常处理逻辑:
- 首先定义权限标识装饰器
import { SetMetadata } from '@nestjs/common'; export const REQUIRED_PERMISSION = 'required_permission'; export const RequirePermission = (permission: string) => SetMetadata(REQUIRED_PERMISSION, permission);
- 实现权限校验守卫
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { GqlExecutionContext } from '@nestjs/graphql'; import { ForbiddenError } from 'apollo-server-express'; @Injectable() export class AuthorizeGuard implements CanActivate { constructor(private reflector: Reflector) {} async canActivate(context: ExecutionContext): Promise<boolean> { const gqlCtx = GqlExecutionContext.create(context); // 获取字段上标注的权限要求 const requiredPermission = this.reflector.get<string>( REQUIRED_PERMISSION, gqlCtx.getHandler() ); // 没有标注权限要求直接放行 if (!requiredPermission) return true; // 复用你原有指令里的校验逻辑 const queryArgs = gqlCtx.getArgs(); const ctx = gqlCtx.getContext(); const instance: IAccessCheck = new AccessCheckClass(); const isAuthorized = await instance.checkAccess(queryArgs, ctx); if (!isAuthorized) { throw new ForbiddenError('Permission denied'); } return true; } }
- 注册全局守卫(无需每个Resolver单独引入)
// app.module.ts的providers中添加 providers: [ { provide: APP_GUARD, useClass: AuthorizeGuard, }, ]
- 在需要校验的Resolver字段上添加权限标注即可
@Query(() => YourType) @RequirePermission('your_permission_key') async yourQuery() { // 原有业务逻辑 }
内容的提问来源于stack exchange,提问作者Elvis Fernandes
相关产品推荐
相关产品推荐

