GraphQL API 中的 HTTP 状态代码处理 [英] HTTP status code handling in GraphQL APIs

查看:22
本文介绍了GraphQL API 中的 HTTP 状态代码处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

A lot of resources say, that GraphQL should always respond with a 200 status code, even when an error occurred:

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

Because GraphQL can return multiple responses in one response, this makes sense. When a user requests two resources in one request, and only has access to the first resource, you can send back the first resource and return a forbidden error for the second resource.

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

However, this is just something I figured out along the way reading docs of multiple GraphQL libraries and blog posts. I didn't find anything about HTTP status codes in the offical specs, here https://spec.graphql.org/ or here https://graphql.org/

  1. 如果我遇到意外的服务器错误,是否可以返回 HTTP 500 状态代码?
  2. 如果凭据错误,是否可以返回 HTTP 401 状态代码?
  3. 我是否应该像这样在 GraphQL 响应的 errors 键中包含 潜在 HTTP 状态代码
  1. Is it ok to return a HTTP 500 status code if I have an unexpected server error?
  2. Is it ok to return a HTTP 401 status code, if credentials are wrong?
  3. Should I include the potential HTTP status code inside the errors key of the GraphQL response like this

{
  "errors" => [{
    "message" => "Graphql::Forbidden",
    "locations" => [],
    "extensions" => {
      "error_class" => "Graphql::Forbidden", "status" => 403
    }
  }]
}

  1. 我是否应该将诸如错误的字段名称之类的常见错误与 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 状态代码时的经验/资源/最佳实践,我会非常高兴.

I'd be great if you could share your experiences / resources / best practises when handling HTTP status codes in GraphQL.

推荐答案

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

GraphQL is transport-agnostic. While GraphQL services are commonly web services that accept requests over HTTP, they can and do accept requests over other transports as well. In fact, a GraphQL service can execute queries with no network requests at all -- all it needs is a query, and, optionally, a variables object and operation name.

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

Because of this, the GraphQL spec isn't concerned with methods, status codes or anything else specific to HTTP (it only mentions HTTP when discussing serialization). Any practices with regard to these things are at best conventions that have either evolved over time or are simply artifacts from some of the original libraries that were written for GraphQL. As such, any kind of answer to your question is going to be mostly based on opinion.

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

That said, because your GraphQL service shouldn't care about how its queries are received, arguably there should be a separation between its code and whatever code is handling receiving the requests and sending back the responses (like an Express app in Node.js). In other words, we could say it's never ok for your resolver code to mutate things like the response's status code. This is the current thinking in the community and most libraries only return one of two codes -- 400 if the request itself is somehow invalid and 200 otherwise.

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

If your entire GraphQL endpoint is guarded by some authentication logic (say your server checks for some header value), then a GraphQL request might come back with a 401 status. But this is something we handle at the web server level, not as part of your schema. It's no different if something went terribly wrong with your web server code and it had to return a 500 status, or the nginx server sitting in front of your returned a 494 (Request header too large), etc.

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

Traditionally, errors encountered during execution should be thrown and that's it. GraphQL extensions can be used to provide additional context when the errors are collected and serialized -- the name of the error, the stack trace, etc. However, it makes little sense to include HTTP status codes with these errors when, again, the errors have nothing to do with HTTP. Doing so unnecessarily mixes unrelated concepts -- if you want to identify the type of error, you're better off using descriptive codes like GENERIC_SERVER, INVALID_INPUT, etc.

然而,有关错误处理的约定也在发生变化.一些服务希望更好地将客户端错误与其他执行错误区分开来.越来越普遍地看到验证错误或其他错误会显示给最终用户作为 data 的一部分返回,而不是被视为执行错误.

However, conventions around error handling are also changing. Some services want to better distinguish client errors from other execution errors. It's becoming more common to see validation errors or other errors that would be shown to the end user to be returned as part of the data instead of being treated like an execution error.

type Mutation {
  login(username: String!, password: String!): LoginPayload!
}

type LoginPayload {
  user: User
  error: Error
}

您可以使用 Shopify 等公共 API 来查看此类有效负载类型的实际效果.这种方法的一个变体是利用联合来表示许多可能的响应.

You can see payload types like these in action with public APIs like Shopify's. A variant on this approach is to utilize unions to represent a number of possible responses.

type Mutation {
  login(username: String!, password: String!): LoginPayload!
}

union LoginPayload = User | InvalidCredentialsError | ExceededLoginAttemptsError

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

The end result is that the client errors are strongly typed and easily distinguishable from other errors that the end user doesn't care about. There's a lot of benefits to adopting these sort of conventions, but whether they are the right fit for your server is ultimately up to you.

这篇关于GraphQL API 中的 HTTP 状态代码处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆