如何在Koa+Apollo的GraphQL认证查询中设置HTTP状态码
在Koa+Apollo中为GraphQL认证查询设置对应HTTP状态码
我之前在Koa+Apollo的项目里正好处理过这个需求——让GraphQL认证查询根据结果返回对应HTTP状态码(200成功/401未授权/422参数缺失),而不是默认的全200。结合你给出的现有配置,给你一套落地的方案:
核心思路
GraphQL本身默认不管请求结果如何都返回200状态码,所以我们需要借助Koa的上下文(ctx),在认证逻辑的不同节点(参数校验、认证结果、错误处理)手动设置对应的HTTP状态码。
步骤1:将Koa上下文注入GraphQL Context
首先修改你的graphqlKoaMiddleware配置,把Koa的ctx传入GraphQL的context中,这样后续在resolver或错误处理里能直接操作HTTP状态:
const graphqlKoaMiddleware = graphqlKoa(ctx => { return ({ schema, formatError: (err) => { // 同步错误对应的HTTP状态码到Koa上下文 let statusCode = 200; if (err.originalError?.statusCode) { statusCode = err.originalError.statusCode; ctx.status = statusCode; } return ({ message: err.message, status: statusCode }); }, context: { ctx, // 注入Koa上下文 stationConnector: new StationConnector(), passengerTypeConnector: new PassengerTypeConnector(), authConnector: new AuthConnector() // 补全你的AuthConnector实例 }, }); });
步骤2:自定义错误类(更可靠的错误判断)
不要用字符串匹配错误信息来判断状态,建议定义专属的错误类,让错误类型更清晰、维护更方便:
// 自定义参数缺失错误(对应422状态码) class ValidationError extends Error { constructor(message = "参数缺失") { super(message); this.name = "ValidationError"; this.statusCode = 422; } } // 自定义未授权错误(对应401状态码) class UnauthorizedError extends Error { constructor(message = "未授权") { super(message); this.name = "UnauthorizedError"; this.statusCode = 401; } }
步骤3:在认证Resolver中处理逻辑并设置状态
在你的认证查询(比如authenticate)的resolver里,完成参数校验、认证逻辑,并抛出对应错误或设置成功状态:
const resolvers = { Query: { authenticate: async (_, args, { ctx, authConnector }) => { // 1. 参数校验:缺失则抛出422错误 if (!args.username || !args.password) { throw new ValidationError("用户名或密码参数缺失"); } // 2. 调用认证连接器验证凭证 const authenticatedUser = await authConnector.verifyCredentials( args.username, args.password ); // 3. 认证失败则抛出401错误 if (!authenticatedUser) { throw new UnauthorizedError("用户名或密码错误,未授权访问"); } // 4. 认证成功,显式设置200状态(虽然默认是200,但显式设置更稳妥) ctx.status = 200; // 返回认证结果(比如JWT令牌、用户信息) return { token: "生成的JWT令牌", user: authenticatedUser }; } } };
额外注意事项
- 确保你已经在Koa中间件链中添加了
koa-bodyparser,否则GraphQL的POST请求参数会无法解析,导致误判参数缺失。 - 如果你的认证是全局要求(而非单个查询),可以把参数校验和认证逻辑移到Koa中间件里提前设置状态码,但这种方式适合所有请求都需要认证的场景。
内容的提问来源于stack exchange,提问作者Stretch0
相关产品推荐
相关产品推荐

