Gin framework series 02: routing and parameters

Keywords: Go curl less network

review

In the previous section, we used the Gin framework to quickly build an interface for GET requests. Today, we will learn how to GET routes and parameters.

Request verb

Students who are familiar with RESTful should know that RESTful is a design style and development mode of network applications. Each URI represents a resource. The client uses POST, DELETE, PUT and GET to add, DELETE and query resources.

Similarly, in addition to these four verbs, the Gin framework provides us with PATCH, OPTION, HEAD, etc. for details, please refer to the irouts interface of the rendergroup.go file.

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
}

Because RenterGroup implements all the request verbs defined by IRoutes, and the Engine type returned by gin.Default inherits RenterGroup, it is very simple to use. You only need to instantiate the object through gin.Default, and then all the routing operations can be used through the object.

func main() {
	router := gin.Default()
	router.POST("/article", func(c *gin.Context) {
		c.String(200, "article post")
	})
	router.DELETE("/article", func(c *gin.Context) {
		c.String(200, "article delete")
	})
	router.PUT("/article", func(c *gin.Context) {
		c.String(200, "article put")
	})
	router.GET("/article", func(c *gin.Context) {
		c.String(200, "article get")
	})
	router.Run()
}

The first parameter of the request verb is the request path, and the second parameter is the function used for logical processing, which can be anonymous or the function name defined elsewhere. Different request verbs can define the same path, only need to switch the verbs to enter the corresponding processing logic.

curl -X PUT http://localhost:8080/article
curl -X POST http://localhost:8080/article
curl -X GET http://localhost:8080/article
curl -X DELETE http://localhost:8080/article

Routing parameters

There are two types of GET requests: one is to add "name=pingye" after the URL, which has a parameter name, and the other is to directly add the parameter value / article/1 in the path, which has no parameter name, so it is necessary to parse the parameter in the code.

protocol://hostname:[port]/path/[query]#fragment

Let's first look at how to play with the parameter value of routing. Here is a question, how to use Gin to get the parameter value 1 of the following link.

The implementation method is very simple. You only need to set a placeholder in the route: id. the colon is the flag of the placeholder. The parameter name after the colon can be customized. Gin will match the route with the request address. If the match is successful, 1 will be assigned as the placeholder: id. you only need to call c.Param to get the id value.

router.GET("/article/:id", func(c *gin.Context) {
  id := c.Param("id")
  c.String(200, id)
})

However, there is a problem with the: id placeholder. If the id parameter value is null, there will be a 404 error prompt.

Then Gin provides another placeholder * id, which can be used to get a null value.

router.GET("/article/*id", func(c *gin.Context) {
  id := c.Param("id")
  c.String(200, id)
})

General parameter

In addition to the parameter values carried by the route, next we look at the traditional GET method.

http://localhost:8080/welcome?firstname=Jane&lastname=Doe

You can get the parameters after the question mark through the c.Query or c.DefaultQuery methods.

router.GET("/welcome", func(c *gin.Context) {
   firstname := c.DefaultQuery("firstname", "pingyeaa")
   lastname := c.Query("lastname")
   c.String(200, firstname+" "+lastname)
})

In the end, both of them call the GetQuery method. The only difference is that DefaultQuery handles the default value.

func (c *Context) DefaultQuery(key, defaultValue string) string {
	if value, ok := c.GetQuery(key); ok {
		return value
	}
	return defaultValue
}

func (c *Context) Query(key string) string {
	value, _ := c.GetQuery(key)
	return value
}

Form parameter

The form content submitted from HTML can also be easily obtained.

router.POST("/form_post", func(c *gin.Context) {
  message := c.PostForm("message")
  nick := c.DefaultPostForm("nick", "anonymous")

  c.JSON(200, gin.H{
    "status":  "posted",
    "message": message,
    "nick":    nick,
  })
})
curl -d "message=pingye" http://localhost:8080/form_post
{"message":"pingye","nick":"anonymous","status":"posted"}

Array type parameter

Sometimes (for example, check box) the front-end page will send the value of array type, which has the same name but different contents.

POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

If it is still a QueryMap method, it is done. The method returns the map type by default.

router.GET("/post", func(c *gin.Context) {
  ids := c.QueryMap("ids")
  c.String(200, ids["a"]+" "+ids["b"])
})
curl http://localhost:8080/post?ids[a]=pingye&ids[b]=hehe
pingye hehe

File upload

In general, file uploads will be directly transferred from the front end to cloud storage service providers, such as Alibaba cloud and qiniu cloud, etc., and less scenarios will be transferred to their own servers. In order to avoid the situation of less hate when books are used, let's have a look.

Gin provides the FormFile method to get the file stream. This method returns a variable of FileHeader type. You can call the Filename property to view the file name.

type FileHeader struct {
   Filename string
   Header   textproto.MIMEHeader
   Size     int64

   content []byte
   tmpfile string
}
router.POST("/upload", func(c *gin.Context) {
  file, _ := c.FormFile("file")
  c.String(200, file.Filename)
})

Through the curl request interface, you can see that it is easy to get the file name.

curl -X POST http://localhost:8080/upload \
  -F "file=@/Users/enoch/Downloads/IMG_9216.JPG" \
  -H "Content-Type: multipart/form-data"
IMG_9216.JPG

Of course, we can not only get the file name, but also use the SaveUploadedFile method to save the file to a certain place. When saving the file, we should ensure that we have the operation permission of the target directory.

router.POST("/upload", func(c *gin.Context) {
		file, _ := c.FormFile("file")
		c.String(200, file.Filename)
		err := c.SaveUploadedFile(file, "/Users/enoch/Desktop/ab.png")
		if err != nil {
			c.String(500, err.Error())
		}
})

Routing packet

When there are major changes to the interface (such as input and output parameters), considering the downward compatibility, an interface will be added generally. However, it is hoped that the name of the new interface is obviously the upgraded version of the old interface, so the version number v1/article can be added in front of the interface name.

v1 := r.Group("v1")
{
  v1.POST("/login", func(c *gin.Context) {
    c.String(200, "v1/login")
  })
  v1.POST("/submit", func(c *gin.Context) {
    c.String(200, "v1/submit")
  })
}

v2 := r.Group("v2")
{
  v2.POST("/login", func(c *gin.Context) {
    c.String(200, "v2/login")
  })
  v2.POST("/submit", func(c *gin.Context) {
    c.String(200, "v2/submit")
  })
}
curl -X POST http://localhost:8080/v1/login
curl -X POST http://localhost:8080/v2/login

Go language library code example, welcome star https://github.com/pingyeaa/golang-examples

Thank you for watching. If you think the article is helpful, please pay attention to the official account of "Ping Yi" and focus on the principle of Go language and technology.

Posted by endurox on Wed, 08 Apr 2020 00:54:57 -0700