You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用express-graphql时,如何在GraphQL报错时修改HTTP状态码?

问题:通过throw Error修改HTTP状态码实现前端本地化错误信息是否可行?如何实现?

我希望在通过throw new Error("this is my error.")抛出错误时修改HTTP状态码,目的是为了在前端返回本地化错误信息,请问是否可行?该如何实现?

我尝试了以下代码,但出现了错误:

在查询/变更中抛出错误:

throw new Error("Not Authorized.")

服务器代码:

graphql_router.use("/", graphqlHTTP((req, res, graphQLParams) => {
    return {
        schema: schema,
        graphiql: process.env.NODE_ENV === 'development',
        contextValue: { ip: req.ip },
        customFormatErrorFn: (error) => {
            try {
                return res.status(500).json({ error: error.message })
            } catch {
                return error;
            }
        },
    }
}));

错误信息:

backend/node_modules/express/lib/response.js:1150
    : JSON.stringify(value);
           ^

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Socket'
    |     property '_writableState' -> object with constructor 'WritableState'
    |     property 'afterWriteTickInfo' -> object with constructor 'Object'
    --- property 'stream' closes the circle
    at JSON.stringify (<anonymous>)
    at stringify (/Users/tarikulkerjs/Desktop/projects/blindlook/backend/node_modules/express/lib/response.js:1150:12)
    at ServerResponse.json (/Users/tarikulkerjs/Desktop/projects/blindlook/backend/node_modules/express/lib/response.js:271:14)
    at graphqlMiddleware (/Users/tarikulkerjs/Desktop/projects/blindlook/backend/node_modules/express-graphql/index.js:198:22)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v18.16.0
[nodemon] app crashed - waiting for file changes before starting...

回答

可行,但你当前的写法存在逻辑错误:customFormatErrorFn的作用是格式化错误对象,返回给GraphQL中间件用于构建最终响应,不是直接发送HTTP响应。你直接调用res.status().json()会返回res对象本身,而res内部包含循环引用的结构(比如Socket、WritableState等),当GraphQL中间件尝试序列化这个返回值时,就会触发你看到的循环引用错误。

正确实现步骤如下:

1. 定义带状态码的自定义错误类

通过自定义错误类,在抛出错误时携带HTTP状态码信息:

class HttpError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.name = 'HttpError'; // 标记错误类型,方便后续识别
  }
}

2. 在Resolver中抛出自定义错误

替换原来的普通Error,抛出我们定义的HttpError:

// 示例:在某个查询/变更的Resolver中
throw new HttpError("Not Authorized.", 401);

3. 配置GraphQL中间件和响应拦截

通过Express中间件拦截GraphQL响应,根据错误中的状态码设置HTTP响应状态;同时在customFormatErrorFn中格式化错误信息,将状态码附加到响应体中:

// 第一步:添加响应拦截中间件,用于设置HTTP状态码
graphql_router.use("/", (req, res, next) => {
  // 保存原生的res.json方法
  const originalJson = res.json;
  res.json = function(data) {
    // 如果响应体包含错误,取第一个错误的状态码设置HTTP状态
    if (data.errors?.length > 0) {
      const statusCode = data.errors[0].extensions?.statusCode || 500;
      res.status(statusCode);
    }
    return originalJson.call(this, data);
  };
  next();
});

// 第二步:配置GraphQL中间件
graphql_router.use("/", graphqlHTTP({
  schema: schema,
  graphiql: process.env.NODE_ENV === 'development',
  contextValue: { ip: req.ip },
  customFormatErrorFn: (error) => {
    const originalError = error.originalError;
    // 识别自定义的HttpError
    if (originalError instanceof HttpError) {
      return {
        message: originalError.message,
        extensions: {
          statusCode: originalError.statusCode
        }
      };
    }
    // 其他未知错误默认返回500状态码
    return {
      message: error.message,
      extensions: {
        statusCode: 500
      }
    };
  }
}));

效果说明

  • 后端抛出错误后,HTTP响应会带上对应的状态码(比如401、500等)
  • 响应体的errors数组中会包含错误信息和状态码,前端可以根据状态码或错误信息展示对应的本地化文本

内容的提问来源于stack exchange,提问作者Tarik Ulker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:44:48