Help us understand the problem. What is going on with this article?

Ginのミドルウェアを使ったエラーハンドリング

はじめに

ginでAPIなんかを書いていると、エラーを返すとき、

if err != nil {
    log.Print(err)
    c.AbortWithJSON(http.StatusInternalServerError, gin.H{
        "Error": "Request failed"
    })
}

のようなコードが(気をつけないと)あちこちに散らばってしまいます。
このままだと、例えばログの表示方法や保存方法が変わることになったときなどに一つひとつ手直ししないといけません。
そこで、ミドルウェアを使ってエラー時の処理をまとめたいと思います。

バージョン

Go : v1.12
gin : v1.5

ミドルウェアについて

ginではミドルウェアを設定でき、通常のハンドラーの前後に任意の処理を入れることができます。

例えば、

func main() {
    r := gin.Default()
    r.Use(someMiddleware())         // ミドルウェアを登録
    r.GET("/", func(c *gin.Context) {
        log.Println("2nd")
        c.JSON(200, gin.H{"Message": "Hello"})
    })
    r.Run()
}

func someMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        log.Println("1st")
        c.Next()                    // ハンドラーを実行
        log.Println("3rd")
    }
}

とすると、ログは、1st, 2nd, 3rdの順番で出てきます。
見ての通り、c.Nextの前に書いたものはハンドラーの前に、後に書いたものはハンドラーの後に実行されます。

エラーハンドリング

エラーの投げ方

gin のコンテキストは、Errorメソッドを使ってエラーが設定できます。
さらにこのメソッドは、元のエラーとTypeやMetaなどの追加情報を含むgin.Error型を返します。
Typeには、gin.ErrorTypePublicErrorTypePrivateなどがあり、デフォルトではErrorTypePrivateです。
Metaの方はinterface{}型なので任意のデータを設定できます。

c.Error(err)        // simple error
c.Error(err).SetType(gin.ErrorTypePublic)
c.Error(err).SetType(gin.ErrorTypePrivate).SetMeta("more data")

リクエストの処理中に何かエラーが起きたときは、AbortWithJSONなどとする代わりに、次のようにコンテキストにエラーを設定して一度ハンドラーから抜けます。

if err != nil {
    c.Error(err).SetType(gin.ErrorTypePublic)
    return
}

エラーミドルウェア

エラーがセットされたコンテキストをミドルウェアで処理するようにします。

次のようなミドルウェアを作成して、r.Use(errorMiddleware())というように登録してください。

func errorMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()

        err := c.Errors.ByType(gin.ErrorTypePublic).Last()
        if err != nil {
            log.Print(err.Err)

            c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
                "Error": err.Error()
            })
        }
    }
}

ここでは、コンテキストにセットされたエラーの中でも、TypeがPublicで、最後のものを取得しています。
エラーを取得した後には、ログの出力やSentryへの送信などといった任意の処理を入れることができます。

また、MetaにHTTPのステータスコードやレスポンスを入れたり、TypeのPrivateとPublicで処理を分けたりなどといったこともできます。

これで、エラーの処理をミドルウェアにまとめることができました!

参考リンク

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした