很多资源都说,GraphQL 应该始终以 200 状态代码响应,即使发生错误:

  • https://www.graph.cool/docs/faq/api-eep0ugh1wa/#how-does-error-handling-work-with-graphcool
  • https://github.com/rmosolgo/graphql-ruby/issues/1130#issuecomment-347373937
  • https://blog.hasura.io/handling-graphql-hasura-errors-with-react/

  • 因为 GraphQL 可以在一个响应中返回多个响应,所以这是有道理的。当用户在一个请求中请求两个资源,并且只能访问第一个资源时,您可以发回第一个资源并为第二个资源返回 forbidden 错误。

    然而,这只是我在阅读多个 GraphQL 库和博客文章的文档的过程中想到的。 我在官方规范中没有找到任何关于 HTTP 状态代码的信息,这里是 https://spec.graphql.org/ 或这里 https://graphql.org/

    所以我还有几个问题:
  • 如果我遇到意外的服务器错误,是否可以返回 HTTP 500 状态代码?
  • 如果凭据错误,是否可以返回 HTTP 401 状态代码?
  • 我是否应该在 GraphQL 响应的 errors 键中包含潜在的 HTTP 状态代码,例如
  • {
      "errors" => [{
        "message" => "Graphql::Forbidden",
        "locations" => [],
        "extensions" => {
          "error_class" => "Graphql::Forbidden", "status" => 403
        }
      }]
    }
    
  • 我是否应该将常见错误(如错误的字段名称)与 HTTP 状态代码 400 Bad Request 匹配?
  • {
      "errors" => [{
        "message" => "Field 'foobar' doesn't exist on type 'UserConnection'",
        "locations" => [{
          "line" => 1,
          "column" => 11
        }],
        "path" => ["query", "users", "foobar"],
        "extensions" => {
          "status" => 400, "code" => "undefinedField", "typeName" => "UserConnection", "fieldName" => "foobar"
        }
      }]
    }
    

    如果您能在 GraphQL 中处理 HTTP 状态代码时分享您的经验/资源/最佳实践,我会很棒。

    最佳答案

    GraphQL 与传输无关。虽然 GraphQL 服务通常是通过 HTTP 接受请求的 Web 服务,但它们也可以并且确实接受通过其他传输的请求。事实上,GraphQL 服务可以在完全没有网络请求的情况下执行查询——它所需要的只是一个查询,以及可选的变量对象和操作名称。

    正因为如此,GraphQL 规范不关心方法、状态代码或任何其他特定于 HTTP 的东西(它只在讨论序列化时提到 HTTP)。关于这些事情的任何实践充其量只是随着时间的推移而演变的约定,或者只是一些为 GraphQL 编写的原始库的产物。因此,对您的问题的任何回答都将主要基于意见。

    也就是说,因为你的 GraphQL 服务不应该关心它的查询是如何接收的,可以说它的代码和处理接收请求和发回响应的任何代码之间应该有一个分离(就像 Node.js 中的 Express 应用程序) .换句话说,我们可以说它是 永远不会 你的解析器代码可以改变诸如响应状态代码之类的东西。这是社区当前的想法,大多数库只返回两个代码之一——如果请求本身无效,则为 400,否则为 200。

    如果您的整个 GraphQL 端点由某些身份验证逻辑保护(假设您的服务器检查某些 header 值),那么 GraphQL 请求可能会返回 401 状态。但这是我们在 Web 服务器级别处理的事情,而不是您架构的一部分。如果您的 Web 服务器代码出现严重错误并且必须返回 500 状态,或者位于您前面的 nginx 服务器返回 494(请求 header 太大)等,这并没有什么不同。

    传统上,执行过程中遇到的错误应该被抛出,仅此而已。 GraphQL 扩展可用于在错误被收集和序列化时提供额外的上下文——错误的名称、堆栈跟踪等。 然而,当再次出现错误时,将 HTTP 状态代码包含在这些错误中是没有意义的与 HTTP 无关。这样做不必要地混合了不相关的概念——如果您想确定错误类型,最好使用描述性代码,如 GENERIC_SERVERINVALID_INPUT 等。

    但是,有关错误处理的约定也在发生变化。一些服务希望更好地将客户端错误与其他执行错误区分开来。越来越常见的情况是,将向最终用户显示的验证错误或其他错误作为 data 的一部分返回,而不是被视为执行错误。

    type Mutation {
      login(username: String!, password: String!): LoginPayload!
    }
    
    type LoginPayload {
      user: User
      error: Error
    }
    

    您可以使用 Shopify 等公共(public) API 来查看此类有效负载类型。这种方法的一个变体是利用联合来表示许多可能的响应。
    type Mutation {
      login(username: String!, password: String!): LoginPayload!
    }
    
    union LoginPayload = User | InvalidCredentialsError | ExceededLoginAttemptsError
    

    最终结果是客户端错误是强类型的,并且很容易与最终用户不关心的其他错误区分开来。采用这些约定有很多好处,但它们是否适合您的服务器最终取决于您。

    关于graphql - GraphQL API 中的 HTTP 状态代码处理,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59729656/

    10-14 04:17