🌮

Ginソースコードリーディング:middlewareとHandlerの関係

2022/12/07に公開

概要

  1. middlewareはビジネスロジックを処理するhandlerと同じものである、gin.Contextを引数としてうける関数である
  2. Requestを処理する時に、middlewareはhandler関数と一緒にgin.Context型のcontextに入れられています。
  3. gin.Contextのnext関数によって、handler関数スライスを順番に消化されています

コード

package main

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.Use(func(context *gin.Context) {
		fmt.Println("middleware")
		fmt.Println("clientIP: ", context.ClientIP())
	})
	r.GET("/ping", func(c *gin.Context) {
		fmt.Println("handle ping handler")
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

動作

/pingにアクセスしたら、以下のログがでます

middle ware
clientIP:  ::1
handle ping handler
[GIN] 2022/12/06 - 21:28:51 | 200 |      1.0347ms |             ::1 | GET      "/ping"

明らかに、定義したmiddlewareの関数が先に実行され、/pingの関数は後実行されてます

main関数実行する時

  1. gin.EngineのUseを呼びます

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L303

  1. 引数はHandlerFunc

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L44

  1. middlewareをgroupのhandlersに入れます。

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/routergroup.go#L65

groupのhandlersはこれです。HandlerFuncのスライスです

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L47

  1. 続いて、gin.Engine.Getを使って、/pingのハンドラーを登録します

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/routergroup.go#L115-L117

  1. HandlerFunc達のマージ

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/routergroup.go#L87-L88

  1. HandlerFunc達のマージをツリーノードに登録

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L331

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/tree.go#L152-L266

リクエスト来る時

  1. まずはgin.EngineのServerHttpに来ます。

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L566

  1. Contextを作成します

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L567-L569

  1. handleHTTPRequest呼びます

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L588

  1. RequestにあるURLからツリーノードを探し出し

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L609

  1. handlerを処理します(重要!)

https://github.com/gin-gonic/gin/blob/cc367f9125516313a4b8d2da1eed5527b5420dbc/gin.go#L613-L618

c.handlers = value.handlers
  1. contextのnextを呼びます

https://github.com/gin-gonic/gin/blob/master/context.go#L171-L177

例えば、デフォルトで入れてあるlogger middlewareはcontext.nextを呼び出しています。

https://github.com/gin-gonic/gin/blob/master/logger.go#L240

サンプルコードのmiddlewareは特にcontext.nextを呼び出していないから、contextのnext関数う内部で押して次のhandlerを呼び出しています

	r.Use(func(context *gin.Context) {
		fmt.Println("middleware")
		fmt.Println("clientIP: ", context.ClientIP())
	})

Discussion