gin 初体验
go
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.Run()
}
gin 路由接受一个 type HandlerFunc func(Context) 类型的函数
New 和 Default 的区别
gin.New 和 gin.Default 都可以创建一个类型为 *gin.Engine 的 router
他们的区别是,gin.Default 加了两个中间件:Logger(), Recovery()
路由分组
路由分组功能是将相同功能的路由进行分组,方便管理
go
r := gin.Default()
r.GET("/goods/list", goodList)
r.GET("/goods/1", goodDetail)
r.POST("goods/add", createGood)
func goodList(c *gin.Context) {}
func goodDetail(c *gin.Context) {}
func createGood(c *gin.Context) {}
go
r := gin.Default()
goodsGroup := r.Group("/goods")
{
goodsGroup.GET("/list", goodList)
goodsGroup.GET("/1", goodDetail)
goodsGroup.POST("/add", createGood)
}
url 中的变量
要获取 url 中的变量,使用 :xxx 的形式
go
func main() {
r := gin.Default()
goodsGroup := r.Group("/goods")
{
goodsGroup.GET("/:id", goodDetail)
}
r.Run()
}
func goodDetail(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{
"message": "id: " + id,
})
}
这种形式会有一个问题,如果有两个路由,一个是 /goods/list,一个是 /goods/:id,那么 /goods/list 会被 /goods/:id 匹配到
解决办法是使用 goodsGroup.GET("/list", goodList),这样就不会有问题了
go
func main() {
r := gin.Default()
goodsGroup := r.Group("/goods")
{
goodsGroup.GET("/list", goodList)
goodsGroup.GET("/:id", goodDetail)
}
r.Run()
}
func goodList(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "list",
})
}
func goodDetail(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{
"message": "id: " + id,
})
}
但是其他路由还是会进入到 /goods/:id 中,比如 /goods/detail
如果只想匹配 id 是数字,需要这样做
通过一个结构体来绑定 uri 中的参数,在注册函数中使用 ShouldBindUri 方法来绑定,如果不是绑定的类型,就返回错误
go
type Params struct {
ID int `uri:"id" binding:"required"`
}
func main() {
r := gin.Default()
goodsGroup := r.Group("/goods")
{
goodsGroup.GET("/:id", goodDetail)
}
r.Run()
}
func goodDetail(c *gin.Context) {
id := c.Param("id")
var params Params
if err := c.ShouldBindUri(¶ms); err != nil {
c.Status(http.StatusBadRequest)
return
}
c.JSON(http.StatusOK, gin.H{
"message": "id: " + id,
})
}
还有一种形式是使用 * 来匹配,比如 /goods/*name
如果访问的路由是 /goods/1/2/3/4,那么 id 就是 1,name 就是 /2/3/4,一般用来访问服务器上的文件
go
goodsGroup.GET("/:id/*name", goodPersoon)
func goodPerson(c *gin.Context) {
id := c.Param("id")
name := c.Param("name")
c.JSON(http.StatusOK, gin.H{
"id": id,
"name": name,
})
}
获取请求中的参数
获取 query 参数,可以使用 c.Query("key"),如果没有这个参数,就返回空字符串
如果想要获取 query 参数,但是没有这个参数,就返回默认值,可以使用 c.DefaultQuery("key", "default")
go
page := c.DefaultQuery("page", "1")
size := c.Query("size")
获取 body 参数,可以使用 c.PostForm("key"),如果没有这个参数,就返回空字符串
如果想要获取 body 参数,但是没有这个参数,就返回默认值,可以使用 c.DefaultPostForm("key", "default")
go
name := c.DefaultPostForm("name", "default")
age := c.PostForm("age")
PostForm 是针对 Content-Type 是 application/x-www-form-urlencoded 和 application/form-data 的情况
如果请求参数是 application/json,那么需要使用 c.ShouldBindJSON/c.BindJSON 方法来获取参数
go
type Body struct {
Name string `json:"name"`
Age int `json:"age"`
}
func goodAdd(c *gin.Context) {
var body Body
c.BindJSON(&body)
c.JSON(http.StatusOK, gin.H{
"name": body.Name,
"age": body.Age,
})
}
表单验证
表单验证可以直接使用 binding 标签来实现
gin 内置了 validator,文档:validator
注册时,需要输入两次密码,可以使用 eqfield 来验证两次密码是否一致
go
type SignUpForm struct {
Age uint8 `json:"age" binding:"required,gte=1,lte=130"`
Name string `json:"name" binding:"required,min=3"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}
r.POST("/signup", func(c *gin.Context) {
var signUpFrom SignUpForm
if err := c.ShouldBindJSON(&signUpFrom); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
})