Inside the Gin framework

Introduction to Gin framework

Gin It is a web framework written in Go (Golang). It's a kind of martini But the API framework with better performance is different from that led by Xie da Beego The web framework, which is more like the Django framework in Python language, contains various components needed to develop a web program.

If you're a performance and productivity seeker, I'm sure you'll fall in love with Gin as much as I do.

At the same time, unlike other Golang API frameworks, the framework community is very active, and the main program is still constantly updated and improved. We know that in general, when selecting an open-source software or related library, the community's activity and project update will be very important (considering later maintenance and performance and feature issues).

On the other hand, the framework officially provides many simple examples for us to run a desired http service quickly, which is a very good start for a student who has just transferred to Golang for business development.

Examples of various HTTP services provided by Gin

Of course, I only went to see the official examples for a long time to quickly get familiar with and realize my own business needs, but when there are some special needs, I usually go to see the official specific implementation to meet the needs. In the long run, it is not only time-consuming and inefficient, so I have an interest in exploring the core source code. I hope to have a deep understanding of the Gin framework through the source code Learning.

Several core structures in Gin framework

We all know that to develop an HTTP service, first we need to start a TCP listener, then we need some column handler s to handle the specific business logic, and finally we need to bind the specific business logic through the HTTP protocol Convention and related methods and URL s to provide specific functions of the HTTP service. Then the corresponding models in the Gin framework are as follows. We will learn the implementation of Gin together.

Several important models in the Gin framework:

  • Engine: it is used to initialize a gin object instance. In this object instance, it mainly contains some basic functions of the framework, such as logs, middleware settings, routing control (groups), handlercontext and other related methods Source file
  • Router: used to define various routing rules and conditions, and register specific routes to a handler implemented by context through HTTP service
  • Context: Context is a very important point in the framework. It allows us to share variables among middleware, manage the whole process, verify the json of request and provide a response body of json. Generally, our business logic processing is also implemented in the whole context reference object
  • Bind: we can get the detailed information of the request in context, such as HTTP request header and request body, but we need to get the corresponding format data according to different HTTP protocol parameters to process the underlying business logic, so we need to use bind related structure method to parse the HTTP data in context

1. Engine structure in gin framework

When we use the framework [gin] (https://github.com/gin-gic/gin) to create an HTTP service, first we need to initialize an instance, which contains some basic properties and instantiation methods of the instance in the Engine structure.

Engine structure:

type Engine struct {
    // Routing group. In the actual development process, we usually use routing group to organize and manage the routes of some columns. For example, / apis/,/v1 /, etc
    RouterGroup
    // Turn on automatic redirection. If the current route does not match, but there is a handler without / beginning, it will be redirected. For example, if the user enters / foo / but there is a / foo, it will be automatically redirected to the handler, and it will return 301 or 307 status code to the client (the difference is GET method and other methods)
    RedirectTrailingSlash bool
    // If this parameter is enabled, the route will try to repair the current request address by itself when there is no handler registration
    // Repair process:
    // 1. The first redundant element will be deleted (.. / or /); 2. The route will find the new path case insensitive; 3. If the corresponding handler can be found normally, the route will be redirected to the correct handler and return 301 or 307. (for example, user access / foo and / / / foo may be redirected to / foo)
    RedirectFixedPath bool
    // If this parameter is enabled, when the current request cannot be routed, the route will check whether other methods are allowed by itself. In this case, it will respond to "Method Not Allowed" and return the status code 405. If no other method is allowed, it will be delegated to the handler of NotFound
    HandleMethodNotAllowed bool
    // Whether to forward client ip
    ForwardedByClientIP    bool
    // If enabled, a header starting with "x-app engine..." will be added to the request
    AppEngine bool
    // If enabled, url.RawPath will be used to find parameters (default: false)
    UseRawPath bool
    // If enabled, the request path will not be escaped. If UseRawPath is false, the parameter is actually true (because URL. Path is used)
    UnescapePathValues bool
    // Value of maxMemory parameter (parameter when ParseMultipartForm of http.Request is called)
    MaxMultipartMemory int64
    // Whether to delete extra backslashes (requests with extra backslashes can be resolved at the beginning)
    RemoveExtraSlash bool
    // Delimiter (render.Delims represents a set of left and right delimiters rendered with HTML, see html/template library for details)
    delims           render.Delims
    // Set the json prefix in Context.SecureJSON China
    secureJsonPrefix string
    // Returns an HTMLRender interface (template used to render two structure types: HTMLProduction and HTMLDebug)
    HTMLRender       render.HTMLRender
    // FuncMap map[string]interface {} in the html/template package is used to define the mapping from name to function
    FuncMap          template.FuncMap
    // Here are some properties defined within the gin framework
    // HandlersChain is an array of HandlerFunc (HandlerFunc is actually a pointer to Context, which will be explained in the next section)
    allNoRoute       HandlersChain
    allNoMethod      HandlersChain
    noRoute          HandlersChain
    noMethod         HandlersChain
    // A collection of temporary access objects is defined here (sync.Pool is thread safe, which is mainly used to cache item s used to reduce GC pressure and create efficient and thread safe idle queues)
    pool             sync.Pool
    // methodTrees is a slice of methodTree (methodTree is a structure containing request method and node pointer, node is a node tree managing path)
    trees            methodTrees
}

HandlerFunc definition:

// It defines a handler that can be used by middleware
type HandlerFunc func(*Context)

How to initialize Engine:

  • New(): this function returns a default reference instance of Engine (automatic redirection is enabled, client ip forwarding is enabled, and request path escaping is forbidden)
  • Default(): call the New() function internally, but add two middleware, Logger and Recovery

Common external methods of Engine:

  • Delims(left, right string) *Engine: specify the left and right separators of the template engine for the created gin instance
  • SecureJsonPrefix(prefix string) *Engine: set securejsonprefix to the created gin instance
  • SetHTMLTemplate(templ *template.Template): this method binds a template engine to the real instance of gin (in fact, the HTMLRender property of engine is set internally)
  • Loadhtmlglobal (pattern string): this method is used to load the HTML template file of glob pattern (similar to the regular in the shell), and then associate the result with the HTML template engine (internal call to SetHTMLTemplate method will match all the results to the template registration)
  • Loadhtmlfiles (files... String): to use this method, you need to specify a set of template file names
  • SetFuncMap(funcMap template.FuncMap): this method will set a FuncMap for template.FuncMap (in fact, engine's FuncMap is set internally)
  • NoRoute (handlers... Handlerfunc): this method adds some handlers to NoRoute, which will return 404 by default (usually in enterprises, 404 we will handle more gracefully, such as static pages for some enterprises)
  • NoMethod (handlers... Handlerfunc): the same as above. This method is used to add a handler to NoMethod. 405 is returned by default
  • Use (middleware... Handlerfunc) irouts: this method is used to bind a global middleware to router. The middleware registered by this method will be included in the handler chain of each request (for example, some loggers or error related middleware can be used here). In the Default() function of the upper initialization instance, engine.Use(Logger(), Recovery()) is used internally to add Load logger and recovery Middleware
  • Routes() (routes RoutesInfo): this method is used to return a route list information RoutesInfo (a routing information RouteInfo contains Method,Path,Handler,HandlerFunc). The underlying layer of this method calls the trees of engine to obtain some necessary information of router
  • Run (addr... String) (error error): this method will bind router to http.Server and enable an HTTP listener to receive HTTP requests. This method is actually a simple implementation of http.ListenAndServe(addr, engine). Note: unless there is an error in this method, goroutine will be called indefinitely to receive requests (as long as the http.servehttmethod is implemented in the engine)
  • Runtls (addr, certfile, keyfile string) (error error): as above, run the service in https mode
  • Rununix (file string) (error): the same as Run(addr) method, run the service through the specified unix socket file
  • Runfd (FD int) (error): the same as the Run(addr) method, run the service through the specified file descriptor (fd)
  • Runlistener (listener net. Listener) (error): the same as Run(addr), run the service through the specified net.Listener
  • ServeHTTP(w http.ResponseWriter, req *http.Request): this method follows the interface specification of http.Handler, which enables gin to call http.ListenAndServe internally to start an HTTP service
  • HandleContext(c *Context): this method will reconfirm an overridden context (it can be implemented through c.Request.URL.Path). It should be noted that this method may cause the cyclic use of context (it will surround you and be used with caution)

2. Router in gin framework

Using the related methods provided in the Engine structure, we can start an HTTP service quickly. But how to expose a URL to realize a simple HTTP data transmission? At this time, we need to use the methods in the Router.

Router related structures in the Gin framework:

  • RouterGroup: this structure is used to configure a route within Gin, and a RouterGroup is used to associate URL prefix and a set of specific handler business logic
  • IRoutes: IRoutes is an interface that defines all routing processing (including some common HTTP methods)
  • IRouter: IRouter is an interface containing all routing processing of a single route and a routing group
// RouterGroup structure
type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

// IRoutes interface
type IRoutes interface {
	Use(...HandlerFunc) IRoutes

	Handle(string, string, ...HandlerFunc) IRoutes
	Any(string, ...HandlerFunc) IRoutes
	GET(string, ...HandlerFunc) IRoutes
	POST(string, ...HandlerFunc) IRoutes
	DELETE(string, ...HandlerFunc) IRoutes
	PATCH(string, ...HandlerFunc) IRoutes
	PUT(string, ...HandlerFunc) IRoutes
	OPTIONS(string, ...HandlerFunc) IRoutes
	HEAD(string, ...HandlerFunc) IRoutes

	StaticFile(string, string) IRoutes
	Static(string, string) IRoutes
	StaticFS(string, http.FileSystem) IRoutes
}

// IRouter interface
type IRouter interface {
	IRoutes
	Group(string, ...HandlerFunc) *RouterGroup
}

Remember that there is a RouterGroup field in our Engine structure in the previous section, which will help us initialize a default RouterGroup instance after we create a gin instance of Engine.

For example, the New() function in the engine structure initializes a gin instance with the following RouterGroup, and registers the gin instance in the engine field of RouterGroup Source file

// https://github.com/gin-gonic/gin/blob/master/gin.go#L129
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},

Common methods for external exposure of RouterGroup structure:

  • Use (middleware... Handlerfunc) Iroutes: register a middleware and return the Iroutes interface
  • Group (relativepath string, handlers... Handlerfunc) * routergroup: the group method creates a new routing group. In general, we will create a common middleware or a route with the same prefix to merge into a routing group
  • BasePath() string: this method is used to return the initial path of a routing group (for example, V: = router. Group ("/ rest/n/v1/api"), then v.BasePath() is "/ rest/n/v1/api")
  • Handle (httpmethod, relativepath string, handlers... Handlerfunc) irouts: this method registers a new handler with the given HTTP method and URL. The last handler should be a real handler, and the others should be middleware shared between different routes. Note: a private method of handle (httpmethod, relativepath string, handlers handlers chain) is called internally to handle the core logic
  • Post (relativepath string, handlers... Handlerfunc) iroutes: this method is a fast implementation of router.handle ("post", path, handle "),
  • Get (relativepath string, handlers... Handlerfunc) irouts: the same as above
  • Delete (relativepath string, handlers... Handlerfunc) irouts: the same as above
  • Patch (relativepath string, handlers... Handlerfunc) iroutes: the same as above
  • Put (relativepath string, handlers... Handlerfunc) irouts: the same as above
  • Options (relativepath string, handlers... Handlerfunc) irouts: the same as above
  • Head (relativepath string, handlers... Handlerfunc) iroutes: the same as above
  • Any (relativepath string, handlers... Handlerfunc) irouts: as above, all HTTP methods will be registered
  • StaticFile(relativePath, filepath string) IRoutes: this method is used to register a single file for routing service local file system, such as: router.StaticFile("favicon.ico", "./resources/favicon.ico")
  • Static(relativePath, root string) IRoutes: this method is used to provide a route specifying the root path of the file system. Internal call group.StaticFS(path,Dir(root,false)) to provide services
  • Staticfs (relativepath string, FS http.FileSystem) irouts: Specifies the file system (http.FileSystem) to create a service

3. Example of gin instance

With the understanding of the above two core models Engine and RouteGroup, we can quickly create a simple HTTP service through the Gin framework.

Default route

# Test example
$ cat case1.go
package main

import (
    "net/http"

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

func main() {
    // Use the Default() function to initialize a gin instance (the reference object of the engine structure)
    // The Default function calls the New() function internally to initialize a gin instance,
    // Using the use (middleware... Handlerfunc) irouts method to register the Logger and Recovery middleware at the same time
    // In the process of New() initializing the gin instance, a RouterGroup with a pathpath of "/" is also initialized by default, which is actually a router instance
    ginObj := gin.Default()
    // Because RouterGroup is an anonymous object in the engine structure, the instantiated engine reference object can directly operate all methods exposed in the RouterGroup structure
    // Here we try to register a route containing all HTTP methods
    // https://github.com/gin-gonic/gin/blob/master/routergroup.go#L133
    // In RouterGroup's various methods exposed to the outside world, the bottom layer calls the handle (httpmethod, relativepath string, handlers... Handlerfunc) irouts method, after which multiple handlers can be passed in to handle the specific business logic. When there are multiple handlers, the last one handles the actual business request, and the previous one handles the middleware and shared components

    // HandlerFunc is actually an anonymous function of func(*Context). Context will be analyzed in the next section
    ginObj.Any("/hello",func(c *gin.Context){
        // The methods related to the context structure will be analyzed in the next section. Here is a simple example
        c.String(http.StatusOK,"Hello BGBiao.")
    })


    // After all the routes are registered, we can use the structure method of gin (the reference object of engine structure) to actually run the HTTP service to receive users' HTTP requests
    // As we said before, this method will call goroutine indefinitely to receive requests unless there is an error
    ginObj.Run("localhost:8080")
}




# Operation example
$ go run case1.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] POST   /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] PUT    /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] PATCH  /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] HEAD   /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] OPTIONS /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] DELETE /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] CONNECT /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] TRACE  /hello                    --> main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on localhost:8080

# Simulate the request (because we registered the routing of all HTTP methods)
$ curl localhost:8080/hello
Hello BGBiao.%
$ curl -X POST  localhost:8080/hello
Hello BGBiao.%
$ curl -X DELETE   localhost:8080/hello
Hello BGBiao.%
$ curl -X TRACE   localhost:8080/hello
Hello BGBiao.%

</br>

Come on, hook up!

wx company number: bgbio, progress together~

Tags: Operation & Maintenance curl github JSON REST

Posted on Sat, 11 Jan 2020 07:38:02 -0500 by readourlines