今後 Go を使用した案件をやる機会があり、現在 Go の勉強中なのですが、今回は案件でも使用するフレームワーク Gin を使用してログインAPIを実装したいと思います。Gin はメモリ利用量が少なく、 reflection を使用していないため高速で動作します。また、余計な機能がないため、学習コストが低く初心者にオススメです。
では早速ログインAPIを作成したいと思います。冒頭の通り今回は Gin を使用するので go get しておいてください。
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r. POST("/login", func(c *gin.Context) { c.String(http.StatusOK, "ログイン完了") }) r. GET("/logout", func(c *gin.Context) { c.String(http.StatusOK, "ログアウトしました") }) r.Run() // listen and server on 0.0.0.0:8080 }
このようにAPIをとても簡単に作成することができます!ただ、このままだとログイン中のセッションが不明なためそのまま使用することはできません。
Gin はミドルウェアによる拡張が簡単に行えるという特徴があります。そこで sessions というミドルウェアを使用してログイン中のセッション管理を行いたいと思います。
まずは sessions のインストールから行います。
$ go get github.com/gin-contrib/sessions
それでは早速コードを書いていきます。
package main import ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() store := cookie.NewStore([]byte("secret")) r.Use(sessions.Sessions("mysession", store)) r. POST("/login", func(c *gin.Context) { // セッションの作成 session := sessions.Default(c) session.Set("loginUser", c.PostForm("userId")) session.Save() c.String(http.StatusOK, "ログイン完了") }) r. GET("/logout", func(c *gin.Context) { // セッションの破棄 session := sessions.Default(c) session.Clear() session.Save() c.String(http.StatusOK, "ログアウトしました") }) r.Run() // listen and server on 0.0.0.0:8080 }
これでログイン中のセッション管理を行うことができるようになりました、これを簡単に実現できたミドルウェアとは何なのか見ていきたいと思います。
Gin におけるミドルウェアとは何なのか、最初のサンプルコードでは一見使用していないように見えますが Gin のデフォルトメソッドで実はミドルウェアをすでに使用しているので詳細を見てみましょう。
早速 gin.Default() の中身を見てみます。
// Default returns an Engine instance with the Logger and Recovery middleware already attached. func Default() *Engine { engine := New() engine.Use(Logger(), Recovery()) return engine }
ここで Use というメソッドを使用し Logger と Recovery というミドルウェアを設定しています。この二つのミドルウェアはどのような機能を持っているか紹介します。
Default で作成した際に入っているミドルウェアになります。リクエストが来たら、下記のようなフォーマットで標準出力にログ出力します。
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s", end.Format("2006/01/02 - 15:04:05"), statusColor, statusCode, reset, latency, clientIP, methodColor, reset, method, path, comment, )
こちらも同じく Default で作成した際に入っているミドルウェアになります。機能としてはパニックが起きた時にリカバーして500を返します。
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. func Recovery() HandlerFunc { return RecoveryWithWriter(DefaultErrorWriter) }
func RecoveryWithWriter(out io.Writer) HandlerFunc { var logger *log.Logger if out != nil { logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) } return func(c *Context) { defer func() { if err := recover(); err != nil { if logger != nil { stack := stack(3) httprequest, _ := httputil.DumpRequest(c.Request, false) logger.Printf("[Recovery] panic recovered:\n%s\n%s\n%s%s", string(httprequest), err, stack, reset) } c.AbortWithStatus(500) } }() c.Next() } }
このように Gin はミドルウェアを使用し機能を拡張していくことができます。
それと、ログインのセッション管理で使用した sessions をもう少し詳しく紹介したいと思います。
sessions は Gin でセッションを扱うためのミドルウェアになります。改めて使い方を紹介したいと思います、サンプルでも書きましたが、まずはインストールから行います。
$ go get github.com/gin-contrib/sessions
使用する場合は以下の import を行います。
import "github.com/gin-contrib/sessions"
githubにあるシングルセッションのサンプルです。
package main import ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() store := cookie.NewStore([]byte("secret")) r.Use(sessions.Sessions("mysession", store)) r.GET("/hello", func(c *gin.Context) { session := sessions.Default(c) if session.Get("hello") != "world" { session.Set("hello", "world") session.Save() } c.JSON(200, gin.H{"hello": session.Get("hello")}) }) r.Run(":8000") }
使用方法を解説するとまず、README に従って store を作成し、以下のように与えてあげるだけで利用可能になります。
store := cookie.NewStore([]byte("secret")) r.Use(sessions.Sessions("mysession", store))
セッションを利用する各関数で session := sessions.Default(c) としてユーザに紐づく session を取得。
// キーに紐づく値を取得 sesion.Get(key string) // キーに値を値を保存 sesion.Set(key string, value []byte) // すべての値を削除 session.Clear() // キーに一致する値を削除 session.Delete(key string)
この session に対して上記の各種操作を行った後に session.Save() でセッションストアに反映します。
if session.Get("hello") != "world" { session.Set("hello", "world") session.Save() }
サンプルだと Get でキー hello の中身をチェックし、 Set で値を入れ Save で変更内容をセッションに保存してます。
すでに用意されているミドルウェアも便利ですが、独自ミドルウェアの実装も勿論できます。
func sampleMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // ルーティング内の処理の前に実行される c.Next() // ルーティング内の処理の後に実行される } }
Gin のHandlerFunc型の関数で作成することで独自のミドルウェアを作成することができます。
Request -> Route Parser -> Middleware -> Route Handler -> Middleware -> Response
Gin のリクエストに対する処理は基本的に上記のようになります、 Route Handler の前後でミドルウェアが呼ばれていますが c.Next() より前の処理が最初に呼ばれ、 c.Next() より後の処理が Route Handler の後に呼ばれます。これにより共通の処理を前後にそれぞれ挟み込むことができるわけです。
独自ミドルウェアを使用する際は他のミドルウェアと同じく Use で設定してください。
r.Use(sampleMiddleware())
今回は Gin の簡単な使い方とほんの一部のミドルウェアの紹介でしたが、他にも JWT や CORS などのミドルウェアもありますので興味がおありの方は是非 Gin を使ってみてください。
https://github.com/gin-gonic/gin
https://github.com/gin-contrib/sessions