Gin framework
Basic installation
1. First, you need to install Go (version 1.10 + is required), and then you can use the following Go command to install Gin.
go get -u github.com/gin-gonic/gin
2. Import it into your code:
import "github.com/gin-gonic/gin"
Usage example:
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 1. Create a route r := gin.Default() // 2. Binding routing rules and executing functions // gin.Context encapsulates request and response r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "hello World!") }) // 3. The listening port is 8080 by default // Run("no port number is specified in it, and the default is 8080") r.Run(":8000") } Copy code
Routing usage
The essence of routing is prefix tree, which is used to realize the function of routing. It is suggested to use postman for test learning, which saves time and effort
Basic use
The setting of routing path follows Restful style (URL positioning and HTTP description operation):
// Set route router := gin.Default() // The first parameter is: path; The second parameter is: specific operation func(c *gin.Context) router.GET("/Get", getting) router.POST("/Post", posting) router.PUT("/Put", putting) router.DELETE("/Delete", deleting) // Port 8080 is started by default router.Run() Copy code
Routing packet
// Both routing groups can be accessed. Braces are used to ensure the specification v1 := r.Group("/v1") { // Access through localhost:8080/v1/hello, and so on v1.GET("/hello", sayHello) v1.GET("/world", sayWorld) } v2 := r.Group("/v2") { v2.GET("/hello", sayHello) v2.GET("/world", sayWorld) } r.Run(":8080") Copy code
Mass routing implementation
When there are many routes, it is recommended to follow the following steps:
- Establish routes package and split different modules into multiple go files
- Each file provides a method that registers and implements all routes
- After that, the main method is registered by calling the method of the file
// Here is the method of opening a router under the routers package func LoadRouter(e *gin.Engine) { e.Group("v1") { v1.GET("/post", postHandler) v1.GET("/get", getHandler) } ... } Copy code
main file implementation:
func main() { r := gin.Default() // Call this method to register routers.LoadRouter(r) routers.LoadRouterXXX(r) // There are more representatives r.Run() } Copy code
If the scale continues to expand, there is a better way to deal with it (it is recommended not to be too large and split the service):
When the project scale is larger, we can follow the following steps:
- Establish routers package and divide modules (packages) internally. Each package has a router.go file, which is responsible for the routing registration of the module
├── routers │ │ │ ├── say │ │ ├── sayWorld.go │ │ └── router.go │ │ │ ├── hello │ │ ├── helloWorld.go │ │ └── router.go │ │ │ └── setup_router.go │ └── main.go Copy code
- Setup setup_router.go file and write the following methods:
type Register func(*gin.Engine) func Init(routers ...Register) *gin.Engine { // Register routing rs := append([]Register{}, routers...) r := gin.New() // Traversal call method for _, register := range rs { register(r) } return r } Copy code
- main.go writes the route to be registered as follows to initialize the route:
func main() { // Set the routing configuration that needs to be loaded r := routers.Init( say.Routers, hello.Routers, // There can be more than one in the back ) r.Run(":8080") } Copy code
Get parameters
Path parameters
: only 1 can be matched, * can match any number
// This rule can match the format of / user/xxx, but cannot match the format of / user / or / user router.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) // This rule can match both / user/xxx / format and / user/xxx/other1/other2 format. Note that it can only be used at the end // Note that if there is no match at the end (there is no /, if there is /, it is still a match), the route without this parameter will be used first, such as the one above (independent of the code order) router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") message := name + " is " + action c.String(http.StatusOK, message) }) Copy code
Get method
- The URL parameters can be obtained through the DefaultQuery() or Query() methods
- DefaultQuery() returns the default value if the parameter does not exist, and Query() returns an empty string if it does not exist
r.GET("/user", func(c *gin.Context) { //Specify default values name := c.DefaultQuery("name", "normal") //Get specific value age := c.Query("age") c.String(http.StatusOK, fmt.Sprintf("hello %s, your age is %s", name, age)) }) Copy code
Post method
r.POST("/form", func(c *gin.Context) { // Set defaults types := c.DefaultPostForm("type", "post") username := c.PostForm("username") password := c.PostForm("password") // You can also use Query to realize the combination of Get + Post name := c.Query("name") c.JSON(200, gin.H{ "username": username, "password": password, "types": types, "name": name, }) }) Copy code
File acquisition
Single file acquisition:
// Limit upload size for forms (default 32 MiB) r.MaxMultipartMemory = 8 << 20 // 8 MiB r.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.String(500, "Error uploading file") } // Upload to specified path c.SaveUploadedFile(file, "C:/desktop/"+file.Filename) c.String(http.StatusOK, "fileName:", file.Filename) }) Copy code
Multiple file acquisition (only the core part is shown):
// Get MultipartForm form, err := c.MultipartForm() if err != nil { c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error())) } // Get all files files := form.File["files"] for _, file := range files { // Save one by one fmt.Println(file.Filename) } c.String(200, fmt.Sprintf("upload ok %d files", len(files))) Copy code
Receive processing
The following examples are based on this structure:
type Login struct { // binding:"required" if the received value is null, an error will be reported User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"` Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"` } Copy code
Set check
If the required field is not received, the error log will tell:
Error:Field validation for 'User' failed on the 'required' tag
Except in the tag setting range, for example
binding:"required,gt=10" => Represents that the value needs to be greater than 10 time_format:"2006-01-02" time_utc:"1" => Time format Copy code
You can also * * customize the verification method: * * gopkg in/go-playground/validator. V8, to be improved
Content type binding (recommended)
When using the Bind method, it should be noted that the tag of the structure should be set first
r.POST("/loginJSON", func(c *gin.Context) { // Declare received variables var login Login // Default binding form format if err := c.Bind(&login); err != nil { // Automatically infer according to the content type in the request header c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Output results c.JSON(http.StatusOK, gin.H{ "status": "200", "user": login.User, "password": login.Password, }) }) Copy code
Specify json binding
Use the ShouldBindJSON method provided by Context. Note that the data sent can only be json
r.POST("/loginJSON", func(c *gin.Context) { // Declare received variables var json Login // Parse the data in the body of the request into the structure in json format if err := c.ShouldBindJSON(&json); err != nil { // If the sent is not in json format, the output: "error": "invalid character '-' in numeric literal" c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Output results c.JSON(http.StatusOK, gin.H{ "status": "200", "user": json.User, "password": json.Password, }) }) Copy code
Response processing
Data return type
There are three common response data types: JSON, XML, and YAML
// 1.JSON r.GET("/someJSON", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Json", "status": 200, }) }) // 2.XML r.GET("/someXML", func(c *gin.Context) { c.XML(200, gin.H{"message": "abc"}) }) // 3.YAML r.GET("/someYAML", func(c *gin.Context) { c.YAML(200, gin.H{"name": "zhangsan"}) }) // 4.protobuf r.GET("/someProtoBuf", func(c *gin.Context) { reps := []int64{1, 2} data := &protoexample.Test{ Reps: reps, } c.ProtoBuf(200, data) }) Copy code
redirect
r.GET("/index", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") }) Copy code
Asynchronous execution
r.GET("/long_async", func(c *gin.Context) { // We need a copy copyContext := c.Copy() // Asynchronous processing go func() { time.Sleep(3 * time.Second) log.Println("Asynchronous execution:" + copyContext.Request.URL.Path) // Note that the redirection task cannot be performed here, or panic }() }) Copy code
Session control
cookie related
r.GET("/getCookie", func(c *gin.Context) { // Gets whether the client carries a cookie cookie, err := c.Cookie("key_cookie") if err != nil { cookie = "cookie" c.SetCookie("key_cookie", "value_cookie", // Parameters 1 and 2: Key & value 60, // Parameter 3: lifetime (seconds) "/", // Parameter 4: Directory "localhost", // Parameter 5: domain name false, // Parameter 6: security related - smart access via https true, // Parameter 7: security related - whether to allow others to obtain their own cookie s through js ) } fmt.Printf("cookie The values are: %s\n", cookie) }) Copy code
session correlation
- Import package: go get -u "GitHub. COM / gin contrib / sessions"
- Add session middleware (the content in the following section will expand the middleware without anxiety)
- Use Get / Set + Save
package main import ( "fmt" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() // Be careful not to disclose the key store := cookie.NewStore([]byte("secret")) //Add session middleware to the route r.Use(sessions.Sessions("mySession", store)) r.GET("/setSession", func(c *gin.Context) { // Set session session := sessions.Default(c) session.Set("key", "value") session.Save() }) r.GET("/getSession", func(c *gin.Context) { // Get session session := sessions.Default(c) v := session.Get("key") fmt.Println(v) }) r.Run(":8080") } Copy code
token related
Generally, for the sake of distribution and security, we will adopt better methods, such as using token authentication to achieve cross domain access, avoid CSRF attacks, and share among multiple services.
middleware
Students who have studied Java can compare middleware to interceptor. Its function is to do some business in advance when processing specific route requests, and to perform some operations after business execution. Such as identity verification and log printing.
Middleware is divided into global middleware and routing middleware. The difference is that the former will act on all routes.
Actually, use Router: = gin Default() defines a route with Logger() and Recovery() by default.
Default Middleware
Gin itself also provides some middleware for us to use:
func BasicAuth(accounts Accounts) HandlerFunc // identity authentication func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc func Bind(val interface{}) HandlerFunc //Intercept request parameters and bind them func ErrorLogger() HandlerFunc //Error log processing func ErrorLoggerT(typ ErrorType) HandlerFunc //Custom type error log processing func Logger() HandlerFunc //Logging func LoggerWithConfig(conf LoggerConfig) HandlerFunc func LoggerWithFormatter(f LogFormatter) HandlerFunc func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc func Recovery() HandlerFunc func RecoveryWithWriter(out io.Writer) HandlerFunc func WrapF(f http.HandlerFunc) HandlerFunc //Http Handlerfunc packaged as middleware func WrapH(h http.Handler) HandlerFunc //Http The handler is wrapped into middleware Copy code
Custom Middleware
The way to customize the middleware is very simple. We only need to implement a function to return gin Parameters of handlerfunc type:
// HandlerFunc is essentially a function, and the input parameter is * gin Context // HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) Copy code
Sample code, complete log printing (output customer ip + send request):
func MyLogMiddleWare() gin.HandlerFunc { return func(c *gin.Context) { fmt.Println("[MyLog] user ip:", c.ClientIP()) fmt.Println("[MyLog] user request:", c.Request) } } func main() { r := gin.Default() // Configuration Middleware r.Use(MyLogMiddleWare()) // Register routing r.GET("/say", func(c *gin.Context) { c.String(200, "request: %s", c.Request) }) r.Run(":8080") } Copy code
Middleware control method
gin provides two functions Abort() and Next(), which are different from each other:
- The next() function will skip the logic after next() in the current middleware, and execute the remaining logic after the execution of the next middleware
- The abort() function terminates the middleware execution after the current middleware, but it will execute the subsequent logic of the current middleware
Examples to better understand:
The order of middleware registration is m1, m2 and m3. If next():
The order of execution is
- In front of m1's next(), m2's next(), m3's next()
- Business logic
- Next() of m3, next() of m2, and next() of m1.
If Abort() is called in the middle of m2, m3 and business logic will not be executed, and only the next() of m2 and the next() of m1 will be executed.
Local Middleware
If our customized middleware only needs to be used on a route, we only need to use this method on the route. You can see the essence from the GET() method.
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodGet, relativePath, handlers) } Copy code
Middleware usage:
//Local middleware usage r.GET("/test", MyLogMiddleWare(), func(c *gin.Context) { // Page reception c.JSON(200, gin.H{"success": "ok"}) }) // Add middleware based on grouping v1 := r.Group("v1", MyLogMiddleWare()) // It can also be written like this // v1.Use(MyLogMiddleWare()) v1.GET("/c1", func(c *gin.Context) { // Page reception c.JSON(200, gin.H{"request": "ok"}) }) v1.GET("/c2", func(c *gin.Context) { // Page reception c.JSON(200, gin.H{"request": "ok"}) }) Copy code
Handling follow-up work
We can also use middleware to handle the follow-up work, and skillfully use next () to realize the follow-up work.
func CalcTimeMiddleWare() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() // Statistical time since := time.Since(start) fmt.Println("Program time:", since) } } func main() { r := gin.Default() // Register routing r.GET("/time", CalcTimeMiddleWare(), func(c *gin.Context) { time.Sleep(2 * time.Second) c.String(200, "ok") }) r.Run(":8080") } Copy code
Output results:
Program time: 2.0002348s [GIN] 2021/09/26 - 15:40:48 | 200 | 2.0002348s | ::1 | GET "/time" Copy code
Authentication Middleware
cookie based authentication middleware can also be implemented.
Core code:
func AuthMiddleWare() gin.HandlerFunc { return func(c *gin.Context) { // Get the client cookie and verify it if cookie, err := c.Cookie("key_cookie"); err == nil { if cookie == "value_cookie" { // If this condition is met, it is passed return } } // Return error c.JSON(http.StatusUnauthorized, gin.H{"error": "err"}) // If the verification fails, subsequent function processing will not be called c.Abort() } } Copy code
Whether the test procedure is executed correctly:
func main() { r := gin.Default() // Simulate login r.GET("/loginIn", func(c *gin.Context) { // Gets whether the client carries a cookie _, err := c.Cookie("key_cookie") if err != nil { c.SetCookie("key_cookie", "value_cookie", // Parameters 1 and 2: Key & value 10, // Parameter 3: lifetime (seconds) "/", // Parameter 4: Directory "localhost", // Parameter 5: domain name false, // Parameter 6: security related - smart access via https true, // Parameter 7: security related - whether to allow others to obtain their own cookie s through js ) c.String(200, "login success") return } c.String(200, "already login") }) // Try to access and add identity authentication middleware. If you have logged in, you can execute it r.GET("/sayHello", AuthMiddleWare(), func(c *gin.Context) { c.String(200, "Hello World!") }) r.Run(":8080") } Copy code
Test steps:
- First, visit localhost:8080/sayHello directly without logging in. Because no cookie is detected, {"error":"err"} will be displayed
- Next, visit localhost:8080/loginIn. The first visit will display: login success. Within the validity period of 10s, the second visit will display: already login
- Within the validity period, visit localhost:8080/sayHello, and Hello World! Will be displayed!, On behalf of successful login
- When the validity period expires, visit localhost:8080/sayHello again, and {"error":"err"} will be displayed, indicating that the identity has expired
Gin project structure
# Gin project structure │ ├── config // Configuration module ├── tools // Tool module ├── vendor // The project relies on other open source project directories │ ├── database // Database module │ └── mysql.go │ ├── middleware // Middleware module │ └── auth.go │ ├── routers // Routing module, similar to controller and setup_router unified registration │ ├── say │ │ ├── say_world.go │ │ └── router.go │ └── setup_router.go │ ├── model // Data model module, struct & database statement (p_xx can also be merged into p) │ ├── p_model.go │ └── p_sql.go │ ├── service // Service module, routing module & reusable logic encapsulation │ ├── say_service │ │ └── say_world.go │ └── main.go // Master file, calling: database initialization, route registration, configuration file initialization Copy code
I hope I can help you, to be continued!
For those who need to get free materials, add a little assistant vx: soxwv # to get materials for free!