This is a creation in Article, where the information may have evolved or changed.
In the Go world, web frameworks are just a dime.
Before the standard library with the battery ( net/http
), after a concise and elegant Gin-gonic (hereinafter referred Gin
to), and then a full stack development of a flower Beego, and so countless.
Using Go for a while, web development has always been used Gin
. Gin
There are a lot of Python
similarities between ideas and frameworks Flask
, which can be called micro-frames .
The Gin includes the following major sections:
Design of a sophisticated routing/middleware system;
Simple and easy to use core context Context
;
Bonus toolset (Json/xml response, data binding and checksum).
This article Gin
is intended to explore the implementation principle of middleware.
Let's look at the following version of the Hello World
Gin
program:
package mainimport ( "log" "net/http" "github.com/gin-gonic/gin")func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.String(http.StatusOK, "%s", "pong!") }) if err := r.Run("0.0.0.0:8080"); err != nil { log.Fatalln(err) }}
This simple Gin
program is enabled by default for two middleware, respectively, Logger()
and Recovery()
.
As we have said before, Context
it is the Gin
core that is constructed as follows:
type Context struct { writermem responseWriter Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 engine *Engine Keys map[string]interface{} Errors errorMsgs Accepted []string}
Which handlers
we can know through the source code is []HandlerFunc
. And its signature is:
type HandlerFunc func(*Context)
So there is no difference between the middleware and our ordinary, right, we can write how to write HandlerFunc
HandlerFunc
a middleware.
So the question comes, how do we solve a request and a response through our middleware?
Let's write a simple middleware analysis:
r.Use(func(c *gin.Context) { log.Println("Request in") // ① c.Next() // next handler func log.Println("Response out") // ② })
The Magic statement appeared, yes c.Next()
, all middleware has Request
and Response
the watershed, that is c.Next()
, otherwise there is no way to pass the middleware.
Let's look at the source code:
func (c *Context) Next() { c.index++ s := int8(len(c.handlers)) for ; c.index < s; c.index++ { c.handlers[c.index](c) }}
A request comes in and is Gin
invoked c.Next()
one time. Because handlers
it is slice
, the middleware is appended to the tail.
This creates a form-like m1(m2(f()))
call chain. As the above number ①② labeled, we will sequentially execute the following call:
m1① -> m2① -> f -> m2② -> m1②
Let's summarize this relationship with one of the following graphs: