This is the first article introduced and used by gin.
I. brief introduction
Gin's official statement is as follows:
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.
Official address: https://github.com/gin-gonic/gin
Simply put, Gin is a Go micro framework, based on httprouter, which provides API services similar to martini (also a go web framework) but with better performance.
Gin has the advantages of fast running speed, good packet routing, reasonable exception capture and error handling, excellent middleware support and json processing support. It is a very recommendable web framework.
With the help of framework development, we can not only save a lot of time from common encapsulation, but also contribute to the formation of good team coding style and coding specifications.
II. Detailed Use
1. installation
- To install the Gin package, you first need to install Go ((version 1.10+) and set up the Go workspace.
- Download and install:
go get -u github.com/gin-gonic/gin
- Import it into your code
import "github.com/gin-gonic/gin"
2. Simple examples:
Run the following code and access it in the browser http://localhost:8080/ping
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
With a few lines of code, a web service is implemented.
First, a routing handle is created using the Default method of gin.
Then, the routing rules and routing functions are bound by HTTP method.
Finally, the Run method of starting the routing listens on the port.
Unlike the routing function of the net/http library, gin is encapsulated to encapsulate both request and response into the context of gin.Context.
3. Common request methods
In addition to GET, gin also supports restful methods such as POST,PUT,DELETE,OPTION, etc.
func main() { // Creates a gin router with default middleware: // logger and recovery (crash-free) middleware router := gin.Default() router.GET("/someGet", getting) router.POST("/somePost", posting) router.PUT("/somePut", putting) router.DELETE("/someDelete", deleting) router.PATCH("/somePatch", patching) router.HEAD("/someHead", head) router.OPTIONS("/someOptions", options) // By default it serves on :8080 unless a // PORT environment variable was defined. router.Run() // router.Run(":3000") for a hard coded port }
4. Obtain the parameters in the path
Gin's route comes from the httprouter library. So httprouter has the same function as gin, but gin does not support routing regular expressions:
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // This handler will match /user/john but will not match /user/ or /user router.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) // Howerer, this one will match /user/john/ and also /user/john/send // if no other routers match /user/john, it will redirect to /user/john/ router.GET("/user/:name/*action", func(c *gin.Context) { name := c.Param("name") action := c.Param("action") message := fmt.Sprintf("%s is %s", name, action) c.String(http.StatusOK, message) }) // For each matched request Context will hold the route definition router.POST("/user/:name/*action", func(c *gin.Context) { c.String(http.StatusOK, c.Request.URL.String()) }) router.Run(":8000") }
- A colon plus a parameter name (such as ": name" above) constitutes the routing parameter. You can read its value using the c.Params method. Of course, this value is string;
- * Number plus a parameter name (such as "action" above) constitutes the routing parameter. Read in the same way as a colon, but the number matches more rules.
Use curl (or Postman, etc.) to access the service, and the test results are as follows:
5. Get the request parameters
The service provided by web is usually the interaction between client and server.
The client sends requests to the server. In addition to routing parameters, there are two other parameters: query string and message body parameters.
query string, a parameter in the form of key1 = Value2 & key2 = Value2 for routing? Of course, this key-value is encoded by urlencode.
5.1. Get Get request parameters
- The client initiates the Get request using the query string parameter, such as:
curl http://127.0.0.1:8000/welcome first name China lastname Nanjing
The reason why we use Chinese is to explain urlencode.
- When dealing with parameters on the server side, it often happens that parameters do not exist. gin also considers whether to provide default values, and gives an elegant scheme:
- The c.DefaultQuery method is used to read the parameters, which provide a default value when the parameters do not exist.
- Use the c.Query method to read the normal parameters, and return empty strings when the parameters do not exist.
Note: When the first name in the above request is an empty string, the server does not use the default Guest value, which is also a value. DefaultQuery only acts on the default value when the key does not exist.
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. // The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") c.String(http.StatusOK, "Hello %s %s", firstname, lastname) }) router.Run(":8000") }
5.2. Getting Post Request Parameters
POST requests submit data to the server, such as form data, which requires the message body parameter. http uses message style to transmit data, which is slightly more complex than query string. There are four common formats:
- application/json (json format data)
- application/x-www-form-urlencoded
- application/xml (xml format data)
- multipart/form-data
By default, c.PostFrom parses parameters of x-www-form-urlencoded or from-data:
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.POST("/form_post", func(c *gin.Context) { message := c.PostForm("message") nick := c.DefaultPostForm("nick", "anonymous") c.JSON(http.StatusOK, gin.H{ "status": "posted", "message": message, "nick": nick, }) }) router.Run(":8000") }
Just as get handles query parameters, post also provides a way to handle default parameters, c.DefaultPostForm. Similarly, for c.PostForm, if the parameter does not exist, an empty string will be obtained.
We used c.String to return the response, and as the name implies, string-type data, content-type is plain or text.
Here we call c.JSON to return JSON type data. gin.H encapsulates the way to generate JSON and is a powerful tool. Using Go, you can write the denomination JSON like a dynamic language. For the implementation of nested json, nested gin. H is enough.
5.3. Mixed use of Get and Post parameters
Sending data to the server is not a post method, but a put method can do the same. At the same time, querystring and body are not separated. Both can be sent at the same time:
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.PUT("/put", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") fmt.Printf("id: %s; page: %s; name: %s; message: %s\n", id, page, name, message) c.JSON(http.StatusOK, gin.H{ "status": "puted", "info": gin.H{ "id": id, "page": page, "name": name, "message": message, }, }) }) router.Run(":8000") }
The example above shows sending data to the server using both query strings and body parameters.
6. File upload
The basic method of sending requests to servers was introduced, in which multipart/form-data is dedicated to file upload. Gin file upload is also very convenient, similar to the original net/http method, except that gin encapsulates the original request into c.Request.
The file name of the uploaded file can be customized by the user, so it may contain illegal strings. For security reasons, the server should unify the file name rules.
6.1. Single file upload
package main import ( "fmt" "io" "net/http" "os" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // Set a lower memory limit for multipart forms (default is 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // single file name := c.DefaultPostForm("name", "template") fmt.Println("name:", name) file, header, err := c.Request.FormFile("file") if err != nil { c.String(http.StatusBadRequest, "bad request: %s", err.Error()) return } filename := header.Filename out, err := os.Create(filename) if err != nil { c.String(http.StatusNotFound, "file create err: %s", err.Error()) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { c.String(http.StatusNotFound, "file copy err: %s", err.Error()) return } c.String(http.StatusCreated, "upload successfully") }) router.Run(":8000") }
Use c.Request.FormFile to parse the client file name attribute. If you do not transfer files, you will throw an error, so you need to deal with this error. One way is to return directly.
Then using the os operation, the file data is copied to the hard disk, and finally returned to the client to upload the successful hint.
Because Go uses UTF-8 encoding format, uploading other encoding format files under windows may cause Chinese scrambling problems, which can be solved by saving them as UTF-8 encoding format files.
6.2. Multi-file upload
Single file upload is very simple, multi-file upload is similar, so-called multi-file upload is a layer of traversal operation, each traversal operation steps are exactly the same as single file upload.
package main import ( "io" "net/http" "os" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // Set a lower memory limit for multipart forms (default is 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/multi/upload", func(c *gin.Context) { // multipart form err := c.Request.ParseMultipartForm(200000) if err != nil { c.String(http.StatusBadRequest, "request body out of memeory: %s", err.Error()) return } form := c.Request.MultipartForm files := form.File["file"] for i := range files { file, err := files[i].Open() if err != nil { c.String(http.StatusBadRequest, "file open err: %s", err.Error()) return } defer file.Close() out, err := os.Create(files[i].Filename) if err != nil { c.String(http.StatusNotFound, "file create err: %s", err.Error()) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { c.String(http.StatusNotFound, "file copy err: %s", err.Error()) return } } c.String(http.StatusCreated, "upload successfully") }) router.Run(":8000") }
Similar to a single file upload, it uses c.Request.MultipartForm to get the file handle, retrieve the file data, and then traverse the reads and writes.
6.3. Form upload
We use curl or integrated tools like Postman to upload pictures. In fact, users upload pictures mostly through forms, or ajax and some requests. Let's show you how to upload form from the web.
We need to write a form page first, so we need to introduce how gin render templates. We saw c.String and c.JSON before. Let's look at the c.HTML method.
First, you need to define a template folder. Then call the c.HTML rendering template and pass the value to the template through gin.H. So far, whether String, JSON or HTML, as well as later XML and YAML, you can see that Gin encapsulated interfaces are simple and easy to use.
Examples are as follows:
- Create a folder template, and then create the html file upload.html inside:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>upload</title> </head> <body> <h3>Single Upload</h3> <form action="/upload", method="post" enctype="multipart/form-data"> <input type="text" value="hello gin" /> <input type="file" name="file" /> <input type="submit" value="upload" /> </form> <h3>Multi Upload</h3> <form action="/multi/upload", method="post" enctype="multipart/form-data"> <input type="text" value="hello gin" /> <input type="file" name="file" /> <input type="file" name="file" /> <input type="submit" value="upload" /> </form> </body> </html>
The upload form is simple and has no parameters. One of the two forms is used for single file upload and the other is used for multiple file upload.
Add the following code to the original code:
router.LoadHTMLGlob("templates/*") router.GET("/upload", func(c *gin.Context) { c.HTML(http.StatusOK, "upload.html", gin.H{}) })
Use router.LoadHTMLGlob method to define template file path.
The complete code is as follows:
package main import ( "fmt" "io" "net/http" "os" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.POST("/upload", upload) // Set a lower memory limit for multipart forms (default is 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/multi/upload", multiUpload) // The relative path is used here, and the actual path is adjusted according to the path of the template. router.LoadHTMLGlob("../templates/*") router.GET("/upload", func(c *gin.Context) { c.HTML(http.StatusOK, "upload.html", gin.H{}) }) router.Run(":8000") } func upload(c *gin.Context) { // single file name := c.DefaultPostForm("name", "template") fmt.Println("name:", name) file, header, err := c.Request.FormFile("file") if err != nil { c.String(http.StatusBadRequest, "bad request: %s", err.Error()) return } filename := header.Filename out, err := os.Create(filename) if err != nil { c.String(http.StatusNotFound, "file create err: %s", err.Error()) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { c.String(http.StatusNotFound, "file copy err: %s", err.Error()) return } c.String(http.StatusCreated, "upload successfully") } func multiUpload(c *gin.Context) { // multipart form err := c.Request.ParseMultipartForm(200000) if err != nil { c.String(http.StatusBadRequest, "request body out of memeory: %s", err.Error()) return } form := c.Request.MultipartForm files := form.File["file"] for i := range files { file, err := files[i].Open() if err != nil { c.String(http.StatusBadRequest, "file open err: %s", err.Error()) return } defer file.Close() out, err := os.Create(files[i].Filename) if err != nil { c.String(http.StatusNotFound, "file create err: %s", err.Error()) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { c.String(http.StatusNotFound, "file copy err: %s", err.Error()) return } } c.String(http.StatusCreated, "upload successfully") }
Access through browser
http://localhost:8000/upload
Then select file upload.
Reference article: Go Language Web Framework--Introduction and Use of Gin