如何修复Nest.js + GraphQL中的错误输出格式问题
Nest.js GraphQL 统一错误格式问题
在Nest.js中使用GraphQL时,数据验证失败后的错误格式不符合预期。已配置全局异常过滤器和gqlErrorHandler,但GraphQL仍将错误信息包裹在data和originalError中,无法得到自定义格式的输出。
现有配置代码
1. GraphQLModule 配置
GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, playground: false, plugins: [ApolloServerPluginLandingPageLocalDefault()], typePaths: ['./**/*.graphql'], resolvers: { DateTime: GraphQLDateTime }, context: ({ req, res }) => ({ req, res }), formatError: gqlErrorHandler, }),
2. gqlErrorHandler 实现
import { GraphQLError } from 'graphql'; export const gqlErrorHandler = (error: GraphQLError) => { return error; };
3. 全局异常过滤器代码
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, } from '@nestjs/common'; import { Request, Response } from 'express'; @Catch() export class AllExceptionsFilter<T> implements ExceptionFilter { catch(exception: T, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; if (['graphql'].includes(host.getType())) { throw new HttpException( this._response(status, request, exception), status, ); } response.status(status).json(this._response(status, request, exception)); } private _response(status: number, request: Request, exception: any) { return { statusCode: status, timestamp: new Date().toISOString(), path: request?.url, method: request?.method, params: request?.params, query: request?.query, exception: { name: exception['name'], message: exception['message'], }, }; } }
当前错误输出
{ "data": {}, "errors": [ { "message": "Http Exception", "locations": [ { "line": 2, "column": 3 } ], "path": [ "createArticle" ], "extensions": { "code": "BAD_REQUEST", "stacktrace": [ "HttpException: Http Exception", " at AllExceptionsFilter.catch (/usr/src/app/src/exceptions/all-exceptions.filter.ts:22:13)", " at ExternalExceptionsHandler.invokeCustomFilters (/usr/src/app/node_modules/@nestjs/core/exceptions/external-exceptions-handler.js:34:32)", ], "originalError": { "statusCode": 400, "exception": { "name": "BadRequestException", "message": "Bad Request Exception" } } } } ] }
解决方案
步骤1:修改全局异常过滤器,适配GraphQL上下文
不要在GraphQL上下文里重新抛出HttpException,而是直接构造自定义错误结构,同时保留GraphQL所需的上下文信息:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, } from '@nestjs/common'; import { Request, Response } from 'express'; import { GqlArgumentsHost } from '@nestjs/graphql'; @Catch() export class AllExceptionsFilter<T> implements ExceptionFilter { catch(exception: T, host: ArgumentsHost) { // 处理GraphQL上下文 if (host.getType() === 'graphql') { const gqlHost = GqlArgumentsHost.create(host); const request = gqlHost.getContext().req as Request; const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; // 直接抛出包含自定义格式的异常,交给formatError处理 throw new HttpException(this._response(status, request, exception), status); } // HTTP上下文原有逻辑不变 const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR; response.status(status).json(this._response(status, request, exception)); } private _response(status: number, request: Request, exception: any) { return { statusCode: status, timestamp: new Date().toISOString(), path: request?.url, method: request?.method, params: request?.params, query: request?.query, exception: { name: exception['name'], message: exception['message'], }, }; } }
步骤2:完善gqlErrorHandler,提取自定义错误格式
在formatError中提取originalError里的自定义结构,覆盖GraphQL默认错误输出:
import { GraphQLError } from 'graphql'; export const gqlErrorHandler = (error: GraphQLError) => { // 提取自定义错误结构 if (error.extensions?.originalError) { const customError = error.extensions.originalError; return { ...customError, locations: error.locations, path: error.path, }; } // 非自定义错误保持默认处理 return error; };
步骤3:确保全局过滤器注册生效
在main.ts中注册全局过滤器:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { AllExceptionsFilter } from './exceptions/all-exceptions.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new AllExceptionsFilter()); await app.listen(3000); } bootstrap();
预期错误输出
修改后,错误将直接返回自定义格式:
{ "errors": [ { "statusCode": 400, "timestamp": "2024-05-20T12:34:56.789Z", "path": "/graphql", "method": "POST", "params": {}, "query": {}, "exception": { "name": "BadRequestException", "message": "Bad Request Exception" }, "locations": [ { "line": 2, "column": 3 } ], "path": [ "createArticle" ] } ], "data": null }
内容的提问来源于stack exchange,提问作者Aslero
相关产品推荐
相关产品推荐

