使用GraphQL(gqlgen),定义错误和错误代码化
首先
(Note: The requested option is provided in Simplified Chinese)
最近,我注意到GraphQL这个词越来越常听到。我认为在新的开发项目中,很多人都选择使用GraphQL作为服务。本文将介绍在GraphQL服务器中引入错误代码概念时,使用gqlgen的简单实现方法。
這篇文章的內容
-
- GraphQL でエラーコード周りどう実装するのか
- gqlgen で実装すると スキーマでエラーコードを実装して、SetErrorPresenter で整形する
在GraphQL中使用错误代码。
无论是GraphQL还是REST等各种形式的API,通常都会预先确定客户端和错误代码,并根据这些代码控制客户端的处理。具体而言,对于发生的错误,会设置特定的代码,并根据这些错误代码确定显示哪个消息或执行哪个跳转等操作。在Firebase中,定义了特定的错误代码,客户端可以根据这些代码判断出现了什么样的错误。
auth/claims-too-large: 传递给setCustomUserClaims()的声明有效负载超过最大允许大小(1,000字节)。
auth/email-already-exists: 提供的电子邮件地址已被现有用户使用。每个用户需要唯一的电子邮件地址。
auth/id-token-expired: 指定的Firebase ID令牌已过期。
-
- 参考: Admin Authentication API エラー
https://firebase.google.com/docs/auth/admin/errors?hl=ja
通過为可能发生的错误代码添加代码,可以清楚地知道发生了什么错误,并使用户更容易采取下一步行动。
要在GraphQL中采用这种错误代码结构,我认为有两种方法(也可能有更多方法)。
-
- エラーコードを全体共通のものとしてスキーマ上で定義する
- mutation や query などに応じてそれぞれの操作個別にエラー型をスキーマ上で定義する
无论是哪种方法,都需要生成一个称为错误代码的特定代码。
在GraphQL模式中定义错误类型,然后为每个错误定义一个名称。由于错误类型只有一个,所以如果该错误在多个位置发生,则需要生成不同的错误代码,即使错误内容相同。
例如,可以明显区分A服务的令牌过期和B服务的令牌过期。
尽管两者的过期时间都已到期,使用相同的错误代码可能是合适的,但由于服务不同,需要设置多个错误代码,就像(a/id-token-expired, b/id-token-expired)这样。(不一定非得这样做。)
如果以这种方式在GraphQL架构中定义错误代码,则如下所示。
type ErrorCode {
serviceA: ErrorCodeServiceAEnum
serviceB: ErrorCodeServiceBEnum
}
在上述例子中,定义了一个名为 ErrorCode 的错误码类型,并在其中定义了每个服务对应的错误码类型。通过这种方式,可以在模式上定义错误码。
使用 gqlgen 返回错误代码
在实际上服务器上进行实现时,我们应该如何去做呢?
因为我经常使用 Go,所以我想要使用一个叫做 gqlgen 的库来返回错误代码。
在 gqlgen 中,可以通过 SetErrorPresenter 来处理错误。
当调用 GraphQL 处理程序时,通过进行此设置并 return err,可以对在处理程序中检测到的错误进行格式化,以确定如何将其作为响应返回。
func (g *GraphQLHandler) GraphQL(c echo.Context) error {
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: g.Resolver}))
//set error presenter
srv.SetErrorPresenter(func(ctx context.Context, e error) *gqlerror.Error {
// この辺で整形する処理を入れる
})
srv.ServeHTTP(c.Response(), c.Request())
*** 省略 ***
}
我通过上述代码示例进行了说明。
简单来说,在 srv.SetErrorPresenter 中格式化返回的错误可以让错误代码更好地返回。
具体的格式化处理,我认为可以使用 errors.Is 等进行格式化。
var ex map[string]interface{}
if errors.Is(err, errUnauthorized) {
ex = map[string]interface{}{
"code": model.ErrorCodeServiceAEnumUnauthorized, // gqlgen で自動生成されたエラー
"status": http.StatusUnauthorized,
}
}
*** この辺に色々定義する ***
return &gqlerror.Error{
Message: err.Error(),
Path: graphql.GetPath(ctx),
Locations: nil,
Extensions: ex,
Rule: "",
}
本來我認為只會列出錯誤代碼 error.Is,但我只會舉一個例子。這是一個返回401錯誤的ServiceA的例子。
我認為使用擴展(extension)返回錯誤代碼和HTTP狀態碼是一種好方法。
我认为通过在扩展中添加代码,使得控制更加容易。
{
"errors": [
{
"message": "hogehoge",
"path": [
"fugafuga"
],
"extensions": {
"code": "SERVICE_A_UNAUTHORIZED",
"status": 401
}
}
],
"data": null
}
最后
非常感谢您阅读到这里。
由于在GraphQL中仅通过响应状态码进行错误处理非常困难,所以我认为在此补充一个类似于错误代码机制的东西可能会更好。
我在这里介绍的内容只是简单的方法,我认为还有很多更好的方法,但如果能对您有所帮助,我会非常高兴。