解决方案(6) http-gin的使用

介绍

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(&param)    // 会根据content-type自动识别json,xml,form
c.BindJSON(&param) // 按照json来解析参数
c.BindXML(&param)
c.BindForm(&param)

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(&param); 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(&param);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/。 统一成一种。
  • 路由不要过度拆分,经常需要通过全文本搜索来匹配到对应的位置。
上一篇:吐血总结!0基础在天津学习web前端的5个小tip!


下一篇:对战秋招小Tip