Introduction and use of go web framework gin

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")
}

  1. 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;
  2. * 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:
    1. The c.DefaultQuery method is used to read the parameters, which provide a default value when the parameters do not exist.
    2. 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:

  1. 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

Keywords: Programming JSON github curl xml

Added by ValdouaD on Sat, 12 Oct 2019 13:56:27 +0300