Train of thought

With the problem in mind, analyze gin’s processing process from the following ideas

  1. How is gin’s middleware and ultimately the respective business processing logic added to the routing?
  2. 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

  1. Gin’s middleware and custom handlers are both funC (C *gin.Context) signatures.
  2. The handlers that are ultimately stored in the route are the handlers with the middleware.
  3. 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.