【问题标题】:GraphQL - How to respond with different status code?GraphQL - 如何用不同的状态码响应?
【发布时间】:2017-08-13 17:34:28
【问题描述】:

我在使用 Graphql 和 Apollo 客户端时遇到问题。

我在使用 REST 时总是创建不同的响应,例如 401 代码,但在这里我不知道如何执行类似的行为。

当我得到响应时,我希望它转到 catch 函数。 我的前端代码示例:

client.query({
  query: gql`
    query TodoApp {
      todos {
        id
        text
        completed
      }
    }
  `,
})
  .then(data => console.log(data))
  .catch(error => console.error(error));

谁能帮帮我?

【问题讨论】:

    标签: javascript graphql apollo react-apollo


    【解决方案1】:

    在 GraphQL(至少在 graphql-js 中)返回错误的方法是在解析函数中抛出错误。因为 HTTP 状态代码特定于 HTTP 传输,而 GraphQL 不关心传输,所以您无法在此处设置状态代码。你可以做的是在你的 resolve 函数中抛出一个特定的错误:

    age: (person, args) => {
      try {
        return fetchAge(person.id);
      } catch (e) {
        throw new Error("Could not connect to age service");
      }
    }
    

    GraphQL 错误会在响应中发送到客户端,如下所示:

    {
      "data": {
        "name": "John",
        "age": null
      },
      "errors": [
        { "message": "Could not connect to age service" }
      ]
    }
    

    如果消息没有足够的信息,您可以为您的 GraphQL 服务器创建一个特殊的错误类,其中包含一个状态代码。为确保响应中包含状态码,您必须在创建中间件时指定 formatError 函数:

    app.use('/graphql', bodyParser.json(), graphqlExpress({ 
        schema: myGraphQLSchema,
        formatError: (err) => ({ message: err.message, status: err.status }),
    }));
    

    【讨论】:

    • 谢谢helfer,很有用。
    • formatError` is deprecated and replaced by `customFormatErrorFn`. It will be removed in version 1.0.0.
    【解决方案2】:

    最近在spec 中添加了关于错误输出的内容:

    GraphQL 服务可以通过关键扩展 提供额外的错误条目。此条目(如果设置)必须有一个映射作为其值。此条目是为实现者保留的,可以在他们认为合适的情况下向错误添加其他信息,并且对其内容没有额外的限制。

    现在使用extensions 字段,您可以自定义机器可读信息到您的errors 条目:

    {
      "errors": [
        {
          "message": "Name for character with ID 1002 could not be fetched.",
          "locations": [ { "line": 6, "column": 7 } ],
          "path": [ "hero", "heroFriends", 1, "name" ],
          "extensions": {
            "code": "CAN_NOT_FETCH_BY_ID",
            "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
          }
        }
      ]
    }
    

    Apollo-Server 的最新版本符合此功能的规范,请查看 Error Handling

    【讨论】:

      【解决方案3】:

      为了补充 Glenn 的回答,here 是 Graphql Spec 的一部分,它定义了如何处理错误。因此,要知道请求是否失败(或部分失败),您可以检查响应根目录中的“errors”键。

      【讨论】:

        【解决方案4】:

        在对此进行了一段时间的试验后,我意识到缺少一些重要的细节。主要是,如果您有一个带有自定义字段的自定义错误对象,上述示例将允许您读取您的自定义属性,因为似乎自定义错误被转换为只有一个消息属性的标准 Error 对象。

        这是我的formatError 函数的样子(注意originalError 属性):

          app.use('/graphql', auth.verifyAccess, graphqlHTTP((req, res) => {
            return {
              schema: makeExecutableSchema({
                typeDefs: typeDefs,
                resolvers: rootResolver
              }),
              graphiql: true,
              formatError: (err) => ({
                message: err.originalError.message || err.message,
                code: err.originalError.code || 500
              }),
            }
          }));
        

        originalError 属性似乎总是​​被设置,但作为安全措施,您可以使用 lodash get 属性。

        我有一个定义的自定义错误类,称为APIError

        class APIError extends Error {
          constructor({ code, message }) {
            const fullMsg = `${code}: ${message}`;
        
            super(fullMsg);
            this.code = code;
            this.message = message;
          }
        }
        
        export default APIError;
        

        在我的解析器中,我抛出异常:

          const e = new APIError({
            code: 500,
            message: 'Internal server error'
          });
        

        【讨论】:

          【解决方案5】:

          我认为关于 graphql 和错误的讨论中缺少的一个问题是从 http 到 gql 的转换中的错误,而这通常是 401 应该发生的地方。

          在转换请求时,您应该将 Authorization 标头(或您使用的任何身份验证方法)转换为用户,如果无法对其进行身份验证,则应返回 HTTP 401 错误 - 这不是图表的一部分或您的 api 规范,只是用户是否可以被验证的问题。您甚至不必检查查询。

          另一方面,gql 层很可能会出现 403 错误(并且可能不会使用 http 状态代码,但这是另一个讨论),因为它可能是非常特定于域的,您必须知道查询决定是否禁止。

          可以使用 HTTP 403 状态告诉用户他根本无法访问 gql api。

          我们在 express/nestjs 中通过在到达 graphql 层之前使用中间件来解决此问题,该中间件可以丰富用户的请求(可能未定义),或者如果用户无法通过身份验证则失败。如果您不提供凭据(或类似信息),我认为不应返回 401。

          【讨论】:

            猜你喜欢
            • 2019-02-16
            • 2020-07-15
            • 2021-07-21
            • 1970-01-01
            • 1970-01-01
            • 2020-10-02
            • 2022-12-20
            • 1970-01-01
            • 2022-06-18
            相关资源
            最近更新 更多