On Gin source code

HTTP processing

Gin is written based on GO's net/http library, which is naturally concurrent. Running the function Engine.run() calls the ListenAndServe function of the net/http Library:

func (engine *Engine) Run(addr ...string) (err error) {
	defer func() { debugPrintError(err) }()

	address := resolveAddress(addr)
	debugPrint("Listening and serving HTTP on %s\n", address)
	err = http.ListenAndServe(address, engine)
	return
}

ListenAndServe will call the serverhttp method in the Handler interface for each request:

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

func ListenAndServe(addr string, handler Handler) error

The Handler interface is implemented by Gin, and the content is not complicated. First, the Context context is constructed, then the handleHTTPRequest method is used to find the handler matching and matching with the Request path (handleHTTPRequest method also executes handler of the middleware. This method is the core method introduced in this article, see below). As follows:

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

Context

In the above, handlehtttprequest passes in the Context. What is its role?
A: it actually encapsulates some functions and stores HTTP context information such as request path, parameter and type. It will be passed to the user-defined request processing function. Users can use it to have many convenient functions. For example, it encapsulates the content type when returning. If you want to return Json body, you only need to call c.JSON(status, bodyContent) to avoid writing a lot of code.

type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
    // Middleware execution function + user execution function
	handlers HandlersChain
    // The subscript of handlers to determine which execution function is currently being called
	index    int8

	engine *Engine
    ...
}

middleware

What is middleware? How does Gin load middleware?

What is middleware

  • Non business technical components are called middleware, and mongo, mysql, redis, mongoDriver and GoORM can all be called middleware.
  • Gin's middleware is relatively narrow. Through the source code, it is found that its middleware only performs custom processing before and after the request. Gin also has many middleware, such as logrus and jwt.

Gin loading Middleware

Add the middleware processing function to the path group by calling the Use method of Engine, that is, to the Handlers slice of RouterGroup:

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	...
	return engine
}

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

Execution of Middleware

Here, the middleware has been added to the handlers of the RouterGroup. When will it be called?
In fact, the handlehtttprequest method mentioned above is the place to call the middleware. First, the code and then discuss:

func (engine *Engine) handleHTTPRequest(c *Context) {
    // something
    ...
    
    t := engine.trees
    for i, tl := 0, len(t); i < tl; i++ {
        if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
        // handlers slice = execution function of each middleware + request processing function.
        handlers, params, tsr := root.getValue(rPath, c.Params, unescape)
		if handlers != nil {
			c.handlers = handlers
			c.Params = params
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
        
        // something
        ...
    }
    // something
    ...
}

func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

Through Next(), we can see that the processing functions of each middleware will be executed in turn, and the request processing function is added to the handlers at last, which is the last execution. In this way, the middleware custom processing is executed before the request processing function is executed.
What should I do if I want to execute the custom processing of the middleware after the request processing function is executed? The middleware can call the Next() function of the Context itself to realize: the logic before Next() in the middleware is executed before the request processing function; The logic after next () is executed after the request processing function.

(just transferred from Java to Go, learn together and make progress together. I think I can praise some achievements. If there is anything wrong, thank you for leaving a message)

Tags: Go

Posted on Wed, 10 Nov 2021 22:57:55 -0500 by digitalflash