With the recommendation of my senior, I will use go to develop the back-end! I've gone through the basic grammar of go before. Now I'm learning the Gin and Gorm framework. I'd like to make a record and hope it will be helpful to you. Of course, because I'm a novice to go, I have something wrong to write. Thank you!
By the way, here's the basic grammar of Go Chinese official documents
Note: most of this article is based on https://www.topgoer.com/gin%E6%A1%86%E6%9E%B6 The learning notes written on this website, and others are the materials and videos found on the Internet. Thank you for the opinions and codes provided by the boss!!
Gin framework
Write hello world with go native https package
package main import ( "fmt" "net/http" //Reference http package ) func sayHello(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprintf(w, "<h1>Hello Golang!</h1>") // b, _ := ioutil.ReadFile("./hello.txt") //ioutil this function can read text files // _, _ = fmt.Fprintf(w, string(b)) } func main() { http.HandleFunc("/hello", sayHello) //visit http://localhost:8080/hello This url err := http.ListenAndServe(":8080",nil) //Listen to port 8080. You don't need to write in the url (because the default is 8080) if err != nil{ fmt.Printf("ERROR:%v\n", err) return } }
Notes: Download Gin framework command line input: go get - u GitHub com/Gin-gonic/Gin
GET request
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() // Return to the default routing engine //When the specified user accesses / hello with a GET request, the sayhello function will be executed r.GET("/hello", func(c *gin.Context) { //It must be * gin Parameters of type context c.JSON(200,gin.H{ "message":"Hello golang!", }) }) //Start service r.Run(":9090") //Change the default port number, and be careful to add: "!!! //r.Run() }
Notes: Restful style requests: GET: GET, POST: create, DELETE: DELETE, PUT: update
POSTMan installation
There are many tutorials on the Internet. I won't mention more here. Let's mention some important points:
1. First of all, the software needs a ladder, including downloading and login registration
2. It is recommended to download the software version
3. After downloading, create a new work space and start the operation
Note: because the template framework needs to read an html file, it does not separate the front and back ends. Personally, I think it is relatively backward. I won't learn it here
How to obtain parameters and files
API acquisition
package main import ( "github.com/gin-gonic/gin" "net/http" "strings" ) func main() { r := gin.Default() r.GET("/user/:name/*action", func(c *gin.Context) { //Crawl the url through anonymous functions name := c.Param("name") //Get the name in the url action := c.Param("action") //Intercept/ action = strings.Trim(action, "/") //Get the last parameter in the url c.String(http.StatusOK, name+" is "+action) }) //The default is to listen to port 8080 r.Run(":8000") }
Get parameters from URL (get method)
Use Query() to get parameters
Use DefaultQuery() to set the default parameters
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { //Specify default values //http://localhost:8080/user Will print out the default value name := c.DefaultQuery("name", "Little dinosaur") c.String(http.StatusOK, fmt.Sprintf("hello %s", name)) }) r.Run() }
When name does not pass in the get parameter, it returns the default small dinosaur:
Of course, parameters can also be passed in:
Get parameters from the form (generally in Post mode)
The form parameters can be obtained through the PostForm() method, which resolves the parameters in x-www-form-urlencoded or from data format by default
(there is also no front and rear end separation here, which is not recommended)
html file:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <form action="http://localhost:8080/form" method="post" action="application/x-www-form-urlencoded"> user name:<input type="text" name="username" placeholder="Please enter your user name"> <br> dense Code:<input type="password" name="userpassword" placeholder="Please enter your password"> <br> <input type="submit" value="Submit"> </form> </body> </html>
go stage
package main // import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.POST("/form", func(c *gin.Context) { types := c.DefaultPostForm("type", "post") username := c.PostForm("username") password := c.PostForm("userpassword") c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types)) }) r.Run() }
Upload files (single or multiple)
html file:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"> Upload file:<input type="file" name="file" > <input type="submit" value="Submit"> </form> </body> </html>
Use the FormFile() function to get the uploaded file
SaveUploadedFile is used to save
go file:
package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() // Limit the upload size of the form to 8MB, and the default is 32MB r.MaxMultipartMemory = 8 << 20 r.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.String(500, "Error uploading picture") } //c.JSON(200, gin.H{"message": file.Header.Context}) c.SaveUploadedFile(file, file.Filename) c.String(http.StatusOK, file.Filename) }) r.Run() }
gin seems to have no function to limit the file size for the time being, so it's not very difficult for us to write one ourselves.
One thing to pay attention to is to change the original c.FormFile into c.request FormFile.
package main import ( "log" "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() //Upload a single file r.POST("/upload", func(c *gin.Context) { _, headers, err := c.Request.FormFile("file") if err != nil { log.Printf("Error when try to get file: %v", err) } //headers.Size gets the file size if headers.Size > 1024*1024*2 { //Limit the size of 2MB c.String(200,"The file is too large") return //Get file type }else if headers.Header.Get("Content-Type") != "image/png" { c.String(200,"Upload only png picture") return }else{ //Go is one of the methods to return json data; Use map (another way is to use structure) my_json := map[string]interface{}{ "1":"success", "2":true, } c.JSON(200,my_json) } //headers. Header. Get ("content type") gets the type of uploaded file c.SaveUploadedFile(headers, "./video/"+headers.Filename) c.String(http.StatusOK, headers.Filename) }) //Upload multiple files //r.POST("/upload", func(c *gin.Context) { // form, err := c.MultipartForm() // if err != nil { // c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error())) // } // //Get all pictures // files := form.File["files"] // //Traverse all pictures // for _, file := range files { // //Save one by one // if err := c.SaveUploadedFile(file, file.Filename); err != nil { // c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error())) // return // } // } // c.String(200, fmt.Sprintf("upload ok %d files", len(files))) //}) r.Run() }
Routing and routing groups
Routing: a method to request data through a path
Different request methods and background processing in routing
r.any() can handle any request
r.NoRoute() can customize 404 pages
Example:
package main import ( "github.com/gin-gonic/gin" ) func main() { // Two middleware loggers () and recovery () are used by default r := gin.Default() r.NoRoute(func(c *gin.Context) { c.JSON(404, gin.H{"msg": "12138"}) }) r.Run() }
Routing group
package main import ( "github.com/gin-gonic/gin" "fmt" ) // helloWorld of gin func main() { // 1. Create a route // Two middleware loggers () and recovery () are used by default r := gin.Default() // Routing group 1, processing GET requests v1 := r.Group("/v1") // {} is a writing standard { v1.GET("/login", login) v1.GET("submit", submit) //Routing groups can be nested //xx := v1.shopGroup("xx") //xx.GET("/oo",my_func) } v2 := r.Group("/v2") { v2.POST("/login", login) v2.POST("/submit", submit) } r.Run(":8000") } func login(c *gin.Context) { name := c.DefaultQuery("name", "jack") c.String(200, fmt.Sprintf("hello %s\n", name)) } func submit(c *gin.Context) { name := c.DefaultQuery("name", "lily") c.String(200, fmt.Sprintf("hello %s\n", name)) }
design sketch:
Note: if you want to import functions in different folders, the first letter of the exported function needs to be capitalized
Parsing and binding of JSON data:
Notes: gin h{...} And map[string]interface {} {...} The effect is the same
package main import ( "github.com/gin-gonic/gin" "net/http" ) // Defines the structure of the received data type Login struct { // binding:"required" modified field. If the received value is null, an error will be reported. It is a required field User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"` Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"` } func main() { // 1. Create a route // Two middleware loggers () and recovery () are used by default r := gin.Default() // JSON binding r.POST("loginJSON", func(c *gin.Context) { // Declare received variables var json Login //Declare json as a struct of type Login // Automatically parse the data in the body of the request into the structure in json format if err := c.ShouldBindJSON(&json); err != nil { //c.ShouldBindUri(...) Is used to parse the parameters in the url //c.Bind(...) Is used to parse the parameters in the form // Return error message // gin.H encapsulates the tool for generating json data c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Determine whether the user name and password are correct if json.User != "root" || json.Pssword != "admin" { c.JSON(http.StatusBadRequest, gin.H{"status": "304"}) return } c.JSON(http.StatusOK, gin.H{"status": "200"}) }) r.Run(":8000") }
Note: the above structure definition uses the structure tag. Due to the limitation of go, the name in the structure can only start with uppercase. Therefore, we can use tag to customize the different parameter names passed in by the structure when requested by different methods
User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"
For example, this sentence should be username when requesting with form
binding:"required" indicates that it is a necessary parameter. If the received value is null, an error will be reported. It is a required field.
redirect
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/index", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.5lmh.com") }) r.Run() }
Asynchronous and synchronous execution
The concept of go process is used here. If you don't know, you can go to Kangkang document
package main import ( "log" "time" "github.com/gin-gonic/gin" ) func main() { // 1. Create a route // Two middleware loggers () and recovery () are used by default r := gin.Default() // 1. Asynchronous 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) }() }) // 2. Synchronization r.GET("/long_sync", func(c *gin.Context) { time.Sleep(3 * time.Second) log.Println("Synchronous execution:" + c.Request.URL.Path) }) r.Run(":8000") }
effect:
middleware
Local Middleware
Functions used for processing between browsers and servers are generally functions that all websites need to use, such as login authentication, permission verification, data paging, logging, time-consuming statistics, etc.
The schematic diagram is similar to the following figure:
package main import ( "fmt" "github.com/gin-gonic/gin" "time" ) func hello(c *gin.Context){ c.JSON(200,gin.H{ "msg":"index", }) } func m1(c *gin.Context){ fmt.Println("Enter verification...") start := time.Now() c.Next() // Call the subsequent processing function (Hello)!!! important //c.Abort() / / prevent calling subsequent processing functions cost := time.Since(start) fmt.Printf("cost:%v\n", cost) } func main() { r := gin.Default() r.GET("/index", m1, hello) r.Run() }
m1 above is a simple middleware function,
It is executed before the hello function, which can determine the logic of the hello function.
design sketch:
Global Middleware
Use r.use(...)
package main import ( "fmt" "github.com/gin-gonic/gin" "time" ) func hello(c *gin.Context){ c.JSON(200,gin.H{ "msg":"index", }) } func m1(c *gin.Context){ fmt.Println("Enter verification...") start := time.Now() c.Next() // Call the subsequent processing function (Hello)!!! important //c.Abort() / / prevent calling subsequent processing functions cost := time.Since(start) fmt.Printf("cost:%v\n", cost) } func main() { r := gin.Default() //As long as the request is made, it must pass m1, which is a middleware (actually a function) r.Use(m1) r.GET("/index", hello) r.Run() }
Easy to use middleware recommendation
website
Session control: (cookie s and sessions)
Search, set and delete cookie s:
package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { // 1. Create a route // Two middleware loggers () and recovery () are used by default r := gin.Default() // The server should give the client a cookie r.GET("cookie", func(c *gin.Context) { // Get whether the client carries cookie s cookie, err := c.Cookie("key_cookie") if err != nil { cookie = "NotSet" // Set cookie s for clients // maxAge int, in seconds // Path, the directory where the cookie is located // domain string, domain name // Is secure smart accessed via https // Does httpOnly bool allow others to obtain their own cookie s through js c.SetCookie("key_cookie", "value_cookie", 60, "/", "localhost", false, true) //If you delete the cookie, you can directly set the duration to 0 or empty //c.SetCookie("key_cookie", "", 60, "/","localhost", false, true) //c.SetCookie("key_cookie", "value_cookie", 0, "/","localhost", false, true) } fmt.Printf("cookie The values are: %s\n", cookie) }) r.Run(":8000") }
Request: http://localhost:8000/cookie View cookies after
Notes: the disadvantages of cookies: 1 Unsafe, plaintext, 2 Increase bandwidth consumption, 3 Can be disabled, 4 Cookie s have an upper limit
Setting, finding and deleting of session (the value is set to nil)
package main import ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" ) func main() { // Initialize a cookie storage object // Something very secret should be your own key, as long as it is not known by others var store = cookie.NewStore([]byte("something-very-secret")) r := gin.Default() //Using middleware, store is the storage engine created before and can be replaced by other engines //mysession is the name that will be stored in the cookie on the browser. The server uses this name to find the corresponding session r.Use(sessions.Sessions("mysession", store)) r.GET("/save", func(c *gin.Context) { session := sessions.Default(c) v := session.Get("count") var count int if v == nil { count = 0 }else { count = v.(int) count++ } session.Set("count",count) session.Save() c.JSON(200, gin.H{"now the count":count}) }) r.GET("/get", func(c *gin.Context) { session := sessions.Default(c) v := session.Get("count") c.JSON(200, gin.H{"the count":v}) }) err := r.Run() if err != nil { return } }
design sketch:
There is no session package in gin, so we need to import other session packages ourselves
Parameter verification
Structural experience certificate:
Write the requirements after the object that defines the structure
package main import ( "fmt" "time" "github.com/gin-gonic/gin" ) //Person .. type Person struct { //Cannot be empty and greater than 10 Age int `form:"age" binding:"required,gt=10"` Name string `form:"name" binding:"required"` Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"` } func main() { r := gin.Default() r.GET("/5lmh", func(c *gin.Context) { var person Person if err := c.ShouldBind(&person); err != nil { c.String(500, fmt.Sprint(err)) return } c.String(200, fmt.Sprintf("%#v", person)) }) r.Run() }
Custom function validation
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type Users struct { Name string `form:"name" json:"name" validate:"required,CustomValidationErrors"`//Include custom functions Age uint8 `form:"age" json:"age" validate:"required,gt=18"` Passwd string `form:"passwd" json:"passwd" validate:"required,max=20,min=6"` Code string `form:"code" json:"code" validate:"required,len=6"` } func main() { // Test incoming data users := &Users{ Name: "admin", Age: 121, Passwd: "126783", Code: "123456", } validate := validator.New() //Register custom functions _=validate.RegisterValidation("CustomValidationErrors", CustomValidationErrors) err := validate.Struct(users) if err != nil { for _, err := range err.(validator.ValidationErrors) { fmt.Println(err)//Key: 'Users.Name' Error:Field validation for 'Name' failed on the 'CustomValidationErrors' tag return } } return } func CustomValidationErrors(fl validator.FieldLevel) bool { return fl.Field().String() != "admin" }
CustomValidationErrors is a self-defined function that can customize filter parameters
Note: be sure to see that the tag behind the structure adds validate instead of binding. This is caused by the problem of the validator version. This problem has been bothering me for a long time. Be sure to see that the validator version is v10, and v9 and v8 may have problems
Here's another big man's summary of how to use this package: website
Other common functions
Log
package main import ( "io" "os" "github.com/gin-gonic/gin" ) func main() { gin.DisableConsoleColor() // Logging to a file. f, _ := os.Create("gin.log") gin.DefaultWriter = io.MultiWriter(f) // If you need to write the log to both the file and the console, use the following code. // gin.DefaultWriter = io.MultiWriter(f, os.Stdout) r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) r.Run() }
design sketch:
Verification Code
///Updating ing
Gorm framework
Notes: English go object relational mapping
Gorm installation
Command line input:
go get -u gorm.io/gorm go get -u gorm.io/driver/sqlite
basic operation
Take mysql as an example
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type Gorm_test struct { ID int //Note: if ID is defined, it will be defaulted as primary key Name string Gender string Hobby string } func main() { mysql_conn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", "root", "1234", "127.0.0.1", 3306, "mydatabase") //Assigned to global variable Db No: = is assigned db, err := gorm.Open("mysql", mysql_conn) if err != nil { panic(err) } fmt.Println("Database initialization succeeded......") //Create a table and associate the structure with the data table db.AutoMigrate(&Gorm_test{}) //Create data row u1 := Gorm_test{1, "Little dinosaur", "male", "piano"} db.Create(&u1) //I suggest adding& fmt.Println("Data added successfully") //query var u Gorm_test //Use the First function to query the First db.First(&u) //Query the first one and assign it to u. note that if you want to modify the structure, you must pass the pointer! fmt.Printf("u:%#v\n", u) //Update data db.Model(&u).Update("hobby", "Percussion") fmt.Println("Successfully changed data") //delete db.Delete(&u) fmt.Println("Data deleted successfully") }
design sketch:
Note: gorm will create a database when we associate the structure. Even if the name of our structure is lowercase and followed by s
Query plus
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type Gorm_test struct { ID int //Note: if ID is defined, it will be defaulted as primary key Name string Gender string Hobby string } func main() { mysql_conn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", "root", "1234", "127.0.0.1", 3306, "mydatabase") //Assigned to global variable Db No: = is assigned db, err := gorm.Open("mysql", mysql_conn) if err != nil { panic(err) } fmt.Println("Database initialization succeeded......") //Create a table and associate the structure with the data table db.AutoMigrate(&Gorm_test{}) var test Gorm_test db.First(&test, 4) //According to the primary key (currently ID), if it is empty, it will return: {0} only the first sign condition can be found fmt.Println(test) var test1 []Gorm_test db.Where("name = ?", "zlz").Find(&test1) //Query all matching records and save them in test1 array. Haishu must add &!!! fmt.Println(test1) }
Note: when no data can be queried, you can specify the field name behind the structure, similar to: ID int `gorm:"column:ID"
Other Go knowledge
Real time loading tool Air
Let me quote the article of the boss directly here: website
Go package management tool Go Mod
See this for specific operation: website
Points to note:
You need main under the home folder go
Each time you create a new project, you can enter from the command line:
go mod init your folder name
go mod tidy
Then I can automatically download the package for you
Configure Go environment on cloud server liunx pagoda panel
Share big guy's website: website
Go's project structure and guide package
Generally, we use Go mod to manage packages
A project usually has only one go mod
For example, if my project is called sisipai, first create a large folder, and then create two small folders called article and quku, as well as a main Go as the main document of the project
There is also a main in article and quku respectively Go file
The first line inside is
//Be careful to capitalize package Article //This bag indicating Article //and package Quku //Indicates that it's Quku's bag
Note: in article and quku, you cannot use idea or go mod,go.sum (a project usually has only one go.mod)
Then switch back to the main folder of Sipai and write
package main //Then you can import import ( "sisipai/Article" "sisipai/Qupu" )
If an error is reported, don't panic. Enter the following in the sisipai folder command line:
go mod init sispai go mod tidy
You can find your bag by yourself!
Run successfully
The Go project runs on the server side
If the server is Linux and you want to put files directly on it, you can compile the files that Linux can run locally and then put them directly on the server.
Method: enter the following on the command line:
set GOARCH=amd64 set GOOS=linux go build .
Then it will generate a file with the same name as your project without suffix, which is even a file that can be executed by linux.
Drag it to your server and give it permission to run:
chmod +x [Your project name]
You can add ". /" to run!
Port problem
Many novice Xiaobai (such as me) didn't know how to make their go file access it after running on the server at the beginning. Today, let's solve this problem.
First of all, your port is like this: (if you want to run on port 8080)
_ = r.Run("0.0.0.0:8080")
Then don't forget to turn on the firewall and release port 8080
Just go on! You can try curl 127.0.0.1:8080 port test, or you can try through the public IP access of your server.
Go language setting cross domain access
website
Cross domain is realized by using cors middleware to change the header. Generally, https should be added to the domain name
Wait ing for perfection