# Gin

# 1. HelloWorld

package main

import (
	"net/http"

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

# 2. gin.New vs gin.Default

gin.Default 有 Logger 和 Recovery。

# 3. 路由

# 3.1 基本路由

  • router.GET
  • router.POST
  • router.DELETE
  • router.PUT

# 3.2 路由分组

  • group1 := router.Group("group1")
    • group1.GET
    • group1.POST
    • group1.DELETE
    • group1.PUT

# 4. 参数

# 4.1 url 中的变量

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {

	r := gin.Default()
	g1 := r.Group("goods")

  // 使用冒号 : 获取 url 中参数 id 的值
	// 使用 * 号来获取 /* 后面的所有路径,在“文件路径”场景使用
	g1.GET("/:id/*action", getGoodsByIdAndAction)

	r.Run()

}

func getGoodsByIdAndAction(c *gin.Context) {
  id := c.Param("id")
	action := c.Param("action")
	c.JSON(http.StatusOK, gin.H{
    "id": id,
		"action": action,
	})
}

访问:http://localhost:8080/goods/22/222/hello/11

输出:

{"action":"/222/hello/11","id":"22"}

# 4.2 参数约束

可以通过 c.ShouldBindUri(&object) 来限制 url 中参数的数据类型

package main

import (
	"net/http"
	"github.com/gin-gonic/gin"
)

// 限制参数类型
type Person struct {
	// required 表示必须带上
	ID   string `uri:"id" binding:"required"`
	Name string `uri:"name" binding:"required"`
}

func main() {

	r := gin.Default()
	r.GET("/:name/:id", func(c *gin.Context) {
		var person Person
    // 参数类型必须满足 Person 结构体中的属性类型
		if err := c.ShouldBindUri(&person); err != nil {
			c.Status(404)
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"name": person.Name,
			"id":   person.ID,
		})
	})
	r.Run()

}

# 4.3 GET 中的 Query

  • c.Query("paramName")
  • c.DefaultQuery("paramName", "defaultValue")
func main() {

	r := gin.Default()

	r.GET("/hello", func(c *gin.Context) {

		id := c.Query("id")
		name := c.Query("name")

		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"id":   id,
		})
	})
	r.Run()

}
image-20220218140205305

# 4.4 POST 中的 form-data

  • c.PostForm("paramName")
func main() {

	r := gin.Default()

	r.POST("/hello", func(c *gin.Context) {

		id := c.PostForm("id")
		name := c.PostForm("name")

		c.JSON(http.StatusOK, gin.H{
			"name": name,
			"id":   id,
		})
	})
	r.Run()

}
image-20220218143459209

# 4.5 POST 中的 body

type Param struct {
	Name string `json: "name"`
	Id   string `json: "id"`
}

func main() {

	r := gin.Default()

	r.POST("/hello", func(c *gin.Context) {
		// 获取 requestBody
		body := c.Request.Body
		// 读取 requestBody
		paramBytes, err := ioutil.ReadAll(body)
		if err != nil {
			c.Status(404)
			return
		}
		var param Param
		// 序列化 body
		err = json.Unmarshal(paramBytes, &param)
		if err != nil {
			c.Status(500)
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"name": param.Name,
			"id":   param.Id,
		})
	})
	r.Run()
}
image-20220218152030962

# 5. 输出

# 5.1 输出 JSON

  • ginContext.JSON(statusCode, gin.H{ ... })

  • ginContext.JSON.JSON(statusCode, structInstance)

  • ginContext.PureJSON(statusCode, gin.H{ ... })

    通常情况下,JSON 会将特殊的 HTML 字符串替换为对应的 unicode 字符,比如 < 替换为 \u003c,如果想输出原样的 HTML,则使用 PureJSON。

# 5.2 输出 ProtoBuf

  • ginContext.ProtoBuf(statusCode, protoObject)

# 6. 表单验证

go-playground/validator (opens new window)

# 6.1 基本验证

若要在请求主体绑定到结构体中,请使用模型绑定,目前支持 JSON、XML、YAML 和标准表单(foo=bar&boo=baz)的绑定。

Gin 使用 go-playground/validator 验证参数。

需要在绑定的字段上设置 tag,比如,绑定格式为 JSON,需要这样设置 json:"fieldName"。此外,Gin 还提供了两套绑定方法:

  • Must bind
    • Methods - BindBindJsonBindXMLBindQueryBindYAML
    • Behavior - 这些方法底层使用 MustBindWith,如果存在绑定错误,请求将会被以下指令终止 c.AbortWithError(400, err).SetTtype(ErrorTypeBind),响应状态码会被设置为 400,请求头 Content-Type 会被设置为 text/plain;charset=utf-8。注意,如果你试图在此之后设置响应码,则会发出一个警告 [Gin-debug][Warning] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行为,请使用 ShouldBind 相关方法。
  • Should bind
    • Methods - ShouldBindShoudBindJSONShoudBindXMLShouldBindQuery
    • Behavior - 这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。

当我们使用绑定方法时,Gin 会根据 Context-Type 推断出使用哪种绑定器。

还可以给字段指定特定规则的修饰符,如果一个字段用 binding: "required" 修饰,并且在绑定时该字段的值为空,那么将返回一个错误。

# 6.2 错误信息中文化

validator 库中提供了错误信息的翻译案例,具体可看:translations-example (opens new window)

# 7. 中间件

  • router.User(middleware ...gin.HandlerFunc)
上次更新: 8/22/2022, 10:48:17 PM