介绍
HTTP服务框架,简单易用。
简称 | 类型 | 含义 |
---|---|---|
r | gin.IRouter | 路由器对象。 |
c | *gin.Context | 请求上下文。 |
param | Param | 路由handler的入参绑定对象 |
1. helloworld
package main
import(
"github.com/gin-gonic/gin"
)
func main() {
r :=gin.Default()
r.GET("/weather", func(c *gin.Context){
c.JSON(200, gin.H{"tip_id":0, "tip": "晴朗"})
})
r.Run(":8080")
}
2. 中间件
- 写法1,在调用Use()之后的路由,都会执行这个中间件里的操作
- 写法2,在路由中间的中间件,只会在该路由生效
- 写法3,仅会在g对应的路由中生效。
var middleware = func(c *gin.Context){
fmt.Printf("recv ip %s\n", c.ClientIP())
// c.Next() 放行
// c.Abort() 拦截住,请求终止并回复
// c.JSON(403, gin.H{"tip":"权限不足","tip_id":403})
}
// 写法1
r.Use(middleware)
// 写法2
r.POST("/hello", middleware, handler)
// 写法3
g := r.Group("")
g.Use(middleware)
g.GET()
g.GET()
3. 绑定参数
参数分为三种: query参数, body参数, param参数
- query参数
GET /user-info/?page=1&size=100
// 如果page有值,则取page,如果没值,则取默认值1
page :=c.DefaultQuery("page",1)
// 如果size有值,则取size,如果没值,则去默认值10
size := c.DefaultQuery("size", 10)
- param参数
r.GET("/user-info/:id/", func(c *gin.Context) {
// 输出19
fmt.Println(c.Param("id"))
})
curl http://localhost:8080/user-info/19/
- body参数
body有三种常见协议,xml,json,form。并且请求方法必须为POST。一般都用json
c.Bind(¶m) // 会根据content-type自动识别json,xml,form
c.BindJSON(¶m) // 按照json来解析参数
c.BindXML(¶m)
c.BindForm(¶m)
4. 回复
c.JSON(200, gin.H{
"tip_id":0,
"tip":"和和"
})
5. 标签 - tag
- json 全golang通用的json标签,json(反)解析时,通过json的tag值匹配
- binding 仅gin里校验该值是否为空值。空值时,BindJSON操作会返回错误。
func GetUserInfo(c *gin.Context) {
type Param struct{
// 匹配{"real_name":"猪"}
// 对不传real_name与{"real_name":""}都会报错
Realname string `json:"real_name" binding:"required"`
}
var param Param
if e:=c.Bind(¶m); e!=nil {
c.JSON(400, gin.H{"message": e.Error()})
return
}
...
}
tag仅使用binding,不使用其它的,减少DSL的产生。
6. 最佳实践
6.1 分包
- 项目入口main.go声明了r后,不同的业务通过以下方式进行分包,避免团队开发时合并冲突。
// shop
shopRouter.HTTPRouter(r)
// pay
payRouter.HTTPRouter(r)
// activity
activityRouter.HttpRouter(r)
// user
userRouter.HTTPRouter(r)
6.2 业务隔离
- 业务和Control隔离开,方便做业务重用。比如http请求改造成grpc请求。
func GetUserInfo(c *gin.Context) {
type Param struct{
GameId int `json:"game_id" binding:"required"`
UserId int `json:"user_id" binding:"required"`
}
var param Param
if e:=c.Bind(¶m);e!=nil {
c.JSON(400, gin.H{"message":e.Error()})
return
}
// 实际获取的方法,必须分包在userService里完成,而不是在本函数完成
userInfo, _ :=userService.GetUserInfo(param.GameId, param.UserId)
c.JSON(200, gin.H{"data":userInfo, "tip_id":0})
}
7. 注意事项
- 业务返回码(statuscode)不要使用403以下的。不利于后期做健康检查,高可用。
- 不要直接基于Control做业务编码,不利于后期做重要服务分离,协议转换。
- 不要在tag里写魔法,顶多用一下json,xml,form,binding这四种tag
- 路由不要时而/user 时而 /user/。 统一成一种。
- 路由不要过度拆分,经常需要通过全文本搜索来匹配到对应的位置。