Train of thought
With the problem in mind, analyze gin’s processing process from the following ideas
- How is gin’s middleware and ultimately the respective business processing logic added to the routing?
- After GIN is started, what is the execution process when a request request arrives?
Define the routing
Gin’s middleware is added using the following method
// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ... HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
Copy the code
Gin’s group can also be passed in at initialization in addition to the above methods
// 1. Use the initialization pass
group1:=g.Group("/g1".func(c *gin.Context) {
log.Println("before group1 middleware1")
c.Next()
log.Println("after group1 middleware1")})// 2
group1.Use(func(context *gin.Context) {
log.Println("before group1 middleware2")
c.Next()
log.Println("after group1 middleware3")})Copy the code
Adding our business processing logic below, you can see that the funC (C *gin.Context) signature is also used here, which is consistent with the signature used by the middleware.
g.Handle("GET"."/h1".func(c *gin.Context) {
c.JSON(http.StatusOK, Response{
Code: 10000,
Msg: "this is h1",
Data: struct{} {},})})Copy the code
By looking at the source code of group.Handle layer by layer, we finally see a combineHandler method
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
Copy the code
The combineHandlers method combines the handlerFunc middleware that we added to the Group. Handlers middleware with the handleFunc containing the business logic that we finally added to the Handle. Handlers are combined into a chain of handlers that are then added to the route along with the relativePath. With each request, the processing chain is ultimately executed.
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
Copy the code
Handle the request
Knowing how the processing logic is eventually added to gin’s routing, how does the middleware and the final business processing logic execute when the front-end initiates an API access and GIN receives a request? The start of the gin
err:=g.Run(": 8080")
iferr ! =nil {
log.Fatal(err)
}
Copy the code
ListenAndServe is the package of HTTP. Familiar with NET/HTTP package, as long as the HTTP.Handler interface is implemented, you can process network requests.
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
defer func(a) { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
Copy the code
A closer look at gin’s serverHttp method implementation shows that the request request is ultimately processed using engine.HandleHttprequest (c). In addition, in the serverHttp method, HTTP.ResponseWriter,* HTTP.Request is encapsulated in a gin custom Context for use by subsequent processes. This is where the Context in the func(c *gin.Context) signature comes from.
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// The context obtained here will be reset to clear the attributes of the previous request
// Get the context object from sync.pool
c := engine.pool.Get().(*Context)
/ / reset HTTP. ResponseWriter
c.writermem.reset(w)
/ / reset * HTTP Request
c.Request = req
// Reset the context object
c.reset()
// Process this request
engine.handleHTTPRequest(c)
// Reuse the context object in sync.pool
engine.pool.Put(c)
}
Copy the code
The focus starts with 22 lines of code to recursively execute middleware and custom business processing logic by resolving the Request method and path, retrieving handlers previously added to the route from the route, assigning to the Context, and executing C.next ().
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
rPath := c.Request.URL.Path
unescape := false
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
rPath = c.Request.URL.RawPath
unescape = engine.UnescapePathValues
}
rPath = cleanPath(rPath)
// Find root of the tree for the given HTTP method
t := engine.trees
// Loop through the prefix tree species to find the processing chain added to the route
for i, tl := 0.len(t); i < tl; i++ {
ift[i].method ! = httpMethod {continue
}
root := t[i].root
// Find route in tree
handlers, params, tsr := root.getValue(rPath, c.Params, unescape)
ifhandlers ! =nil {
// Handle chain assignments to context instances
c.handlers = handlers
c.Params = params
c.Next()
c.writermem.WriteHeaderNow()
return
}
ifhttpMethod ! ="CONNECT"&& rPath ! ="/" {
if tsr && engine.RedirectTrailingSlash {
redirectTrailingSlash(c)
return
}
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
return}}break
}
if engine.HandleMethodNotAllowed {
for _, tree := range engine.trees {
if tree.method == httpMethod {
continue
}
if handlers, _, _ := tree.root.getValue(rPath, nil, unescape); handlers ! =nil {
c.handlers = engine.allNoMethod
serveError(c, http.StatusMethodNotAllowed, default405Body)
return
}
}
}
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
Copy the code
C. next (), which executes the handlerChain, the processing chain registered in the route, in order.
conclusion
- Gin’s middleware and custom handlers are both funC (C *gin.Context) signatures.
- The handlers that are ultimately stored in the route are the handlers with the middleware.
- The Gin Context contains the Handlers handler chain, the HTTP ResponseWriter and Request, and encapsulates methods for retrieving parameters and returning interfaces throughout the process.