LoginSignup
27
16

More than 3 years have passed since last update.

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

Posted at

はじめに

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で処理を分けたりなどといったこともできます。

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

参考リンク

27
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
16