GraphQL API 中的 HTTP 状态代码处理
HTTP status code handling in GraphQL APIs
很多资源说,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 可以在一个响应中 return 多个响应,这是有道理的。当用户在一个请求中请求两个资源,并且只能访问第一个资源时,您可以发回第一个资源,并 return 第二个资源的 forbidden
错误。
然而,这只是我在阅读多个 GraphQL 库的文档和博客文章的过程中想到的。 我在官方规范中没有找到任何关于 HTTP 状态代码的信息,这里 https://spec.graphql.org/ or here https://graphql.org/
所以我还有几个问题:
- 如果我遇到意外的服务器错误,return HTTP 500 状态代码是否可以?
- 如果凭据错误,return 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 是 transport-agnostic。虽然 GraphQL 服务通常是接受 HTTP 请求的 Web 服务,但它们也可以并且确实接受其他传输方式的请求。事实上,GraphQL 服务可以在根本没有网络请求的情况下执行查询——它所需要的只是一个查询,以及一个可选的变量 object 和操作名称。
因此,GraphQL 规范不关心方法、状态代码或任何其他特定于 HTTP 的内容(它只在讨论序列化时提到 HTTP)。关于这些事情的任何实践充其量只是随时间演变的惯例,或者只是一些为 GraphQL 编写的原始库的产物。因此,对您问题的任何回答都将主要基于意见。
也就是说,因为您的 GraphQL 服务不应该关心它的查询是如何被接收的,可以说它的代码和处理接收请求的任何代码之间应该有一个分离并发回响应(如 Node.js 中的 Express 应用程序)。换句话说,我们可以说 never 可以让您的解析器代码改变诸如响应的状态代码之类的东西。这是社区当前的想法,大多数图书馆只是 return 两个代码之一——如果请求本身无效则为 400,否则为 200。
如果您的整个 GraphQL 端点受到某种身份验证逻辑的保护(比如您的服务器检查某些 header 值),那么 GraphQL 请求可能返回 401 状态。但这是我们在 Web 服务器级别处理的事情,而不是作为您的架构的一部分。如果您的 Web 服务器代码出现严重错误并且它必须 return 500 状态,或者位于您 returned 494 前面的 nginx 服务器(请求 header太大)等
传统上,应该抛出执行过程中遇到的错误,仅此而已。 GraphQL 扩展可用于在收集和序列化错误时提供额外的上下文——错误的名称、堆栈跟踪等。但是,包含 HTTP 状态没有什么意义带有这些错误的代码,同样,这些错误与 HTTP 无关。这样做会不必要地混淆不相关的概念——如果您想识别错误类型,最好使用描述性代码,例如 GENERIC_SERVER
、INVALID_INPUT
等
但是,有关错误处理的约定也在发生变化。某些服务希望更好地区分 客户端错误 与其他执行错误。越来越常见的情况是,将向最终用户显示的验证错误或其他错误被 returned 作为 data
的一部分,而不是被视为执行错误。
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
type LoginPayload {
user: User
error: Error
}
您可以看到像这样的负载类型与 public API(如 Shopify 的)一起使用。这种方法的一个变体是利用并集来表示许多可能的响应。
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
union LoginPayload = User | InvalidCredentialsError | ExceededLoginAttemptsError
最终结果是客户端错误是强类型的,很容易与最终用户不关心的其他错误区分开来。采用这些约定有很多好处,但它们是否适合您的服务器最终取决于您。
很多资源说,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 可以在一个响应中 return 多个响应,这是有道理的。当用户在一个请求中请求两个资源,并且只能访问第一个资源时,您可以发回第一个资源,并 return 第二个资源的 forbidden
错误。
然而,这只是我在阅读多个 GraphQL 库的文档和博客文章的过程中想到的。 我在官方规范中没有找到任何关于 HTTP 状态代码的信息,这里 https://spec.graphql.org/ or here https://graphql.org/
所以我还有几个问题:
- 如果我遇到意外的服务器错误,return HTTP 500 状态代码是否可以?
- 如果凭据错误,return 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 是 transport-agnostic。虽然 GraphQL 服务通常是接受 HTTP 请求的 Web 服务,但它们也可以并且确实接受其他传输方式的请求。事实上,GraphQL 服务可以在根本没有网络请求的情况下执行查询——它所需要的只是一个查询,以及一个可选的变量 object 和操作名称。
因此,GraphQL 规范不关心方法、状态代码或任何其他特定于 HTTP 的内容(它只在讨论序列化时提到 HTTP)。关于这些事情的任何实践充其量只是随时间演变的惯例,或者只是一些为 GraphQL 编写的原始库的产物。因此,对您问题的任何回答都将主要基于意见。
也就是说,因为您的 GraphQL 服务不应该关心它的查询是如何被接收的,可以说它的代码和处理接收请求的任何代码之间应该有一个分离并发回响应(如 Node.js 中的 Express 应用程序)。换句话说,我们可以说 never 可以让您的解析器代码改变诸如响应的状态代码之类的东西。这是社区当前的想法,大多数图书馆只是 return 两个代码之一——如果请求本身无效则为 400,否则为 200。
如果您的整个 GraphQL 端点受到某种身份验证逻辑的保护(比如您的服务器检查某些 header 值),那么 GraphQL 请求可能返回 401 状态。但这是我们在 Web 服务器级别处理的事情,而不是作为您的架构的一部分。如果您的 Web 服务器代码出现严重错误并且它必须 return 500 状态,或者位于您 returned 494 前面的 nginx 服务器(请求 header太大)等
传统上,应该抛出执行过程中遇到的错误,仅此而已。 GraphQL 扩展可用于在收集和序列化错误时提供额外的上下文——错误的名称、堆栈跟踪等。但是,包含 HTTP 状态没有什么意义带有这些错误的代码,同样,这些错误与 HTTP 无关。这样做会不必要地混淆不相关的概念——如果您想识别错误类型,最好使用描述性代码,例如 GENERIC_SERVER
、INVALID_INPUT
等
但是,有关错误处理的约定也在发生变化。某些服务希望更好地区分 客户端错误 与其他执行错误。越来越常见的情况是,将向最终用户显示的验证错误或其他错误被 returned 作为 data
的一部分,而不是被视为执行错误。
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
type LoginPayload {
user: User
error: Error
}
您可以看到像这样的负载类型与 public API(如 Shopify 的)一起使用。这种方法的一个变体是利用并集来表示许多可能的响应。
type Mutation {
login(username: String!, password: String!): LoginPayload!
}
union LoginPayload = User | InvalidCredentialsError | ExceededLoginAttemptsError
最终结果是客户端错误是强类型的,很容易与最终用户不关心的其他错误区分开来。采用这些约定有很多好处,但它们是否适合您的服务器最终取决于您。