Skip to content

GIN 入门手册

1398字约5分钟

Go

2023-01-31

简介

Gin 是一个用 Go (Golang) 编写的 Web 框架. 因为开发语言从Python切换到Go, 所以用Gin来替代原先的Flask.

使用包 : import "github.com/gin-gonic/gin"

常用的结构和代码

最简单的Demo

package main

import "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() // 监听并在 0.0.0.0:8080 上启动服务
}

Group路由组

类似于Flask中的blueprint, 用于组织路由地址的层次.

func main() {
  router := gin.Default()

  // 简单的路由组: v1
  v1 := router.Group("/v1")
  {
        v1.GET("/login", loginHandler()) //url: http://0.0.0.0:8080/v1/login
        v1.GET("/setting", settingHandler()) //url: http://0.0.0.0:8080/v1/setting
  }

  router.Run(":8080")
}

//定义中间件
func loginHandler() gin.HandlerFunc {
  return func(c *gin.Context) {
    c.JSON(200, gin.H{
      "message": "successfully login!!",
    })
  }
}
func settingHandler() gin.HandlerFunc {
  return func(c *gin.Context) {
    c.JSON(200, gin.H{
      "message": "successfully setting!!",
    })
  }
}

中间件

中间件是Gin中非常非常重要的一个部分!!!

中间件的作用:

  1. Web请求到到达我们定义的HTTP请求处理方法之前, 拦截请求并进行相应处理(比如: 权限验证, 数据过滤等), 这个可以类比为前置拦截器或前置过滤器,
  2. 在我们处理完成请求并响应客户端时, 拦截响应并进行相应的处理(比如: 添加统一响应部头或数据格式等), 这可以类型为后置拦截器或后置过滤器.

中间件实际上就是一个以gin.Context为形参的函数

Gin中有许多内置的中间件

func BasicAuth(accounts Accounts) HandlerFunc
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc
func ErrorLogger() HandlerFunc
func ErrorLoggerT(typ ErrorType) HandlerFunc
func Logger() HandlerFunc
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc
func WrapH(h http.Handler) HandlerFunc

使用示例:

func main() {
  r := gin.Default()

  r.Use(gin.BasicAuth(gin.Accounts{
    "admin": "123456",
  }))

  r.GET("/", func(c *gin.Context) {
    c.JSON(200, "通过验证, 欢迎进入首页")
  })

  r.Run(":8080")
}

中间件的自定义

func MyMiddleware(){
    return func(c *gin.Context){
        //do something
    }
}

//使用时
//Default意味着全局中间件
router = gin.Default() 
router.Use(MyMiddleware()) //注意MyMiddleware有括号

//Group意味着分组使用
adminGroup := r.Group("/admin")
adminGroup.Use(gin.BasicAuth(gin.Accounts{
    "admin": "123456"
}))
adminGroup.GET("/index", func(c *gin.Context) {
    c.JSON(200, "后台首页")
})

数据传递

当我们在中间件拦截并预先处理好数据之后, 要如何将数据传递我们定义的处理请求的HTTP方法呢?可以使用gin.Context中的Set()方法, 其定义如下, Set()通过一个key来存储作何类型的数据, 方便下一层处理方法获取.

func MyMiddleware(c *gin.Context){
    c.Set("mykey",10)
}

router := gin.New()
router.GET("test",MyMiddleware,func(c *gin.Context){
c.GetInt("mykey")
})

对应的方法有:

func (c *Context) GetBool(key string) (b bool)
func (c *Context) GetDuration(key string) (d time.Duration)
func (c *Context) GetFloat64(key string) (f64 float64)
func (c *Context) GetInt(key string) (i int)
func (c *Context) GetInt64(key string) (i64 int64)
func (c *Context) GetString(key string) (s string)
func (c *Context) GetStringMap(key string) (sm map[string]interface{})
func (c *Context) GetStringMapString(key string) (sms map[string]string)
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
func (c *Context) GetStringSlice(key string) (ss []string)
func (c *Context) GetTime(key string) (t time.Time)

拦截请求与后置拦截

1. 拦截请求

比如我们有些请求需要用户登录或者需要特定权限才能访问, 这时候便可以中间件中做过滤拦截, 当用户请求不合法时, 可以使用下面列出的gin.Context的几个方法中断用户请求:

下面三个方法中断请求后, 直接返回200, 但响应的body中不会有数据.

func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)

使用AbortWithStatusJSON()方法, 中断用户请求后, 则可以返回json格式的数据.

func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
2. 后置拦截

前面我们讲的都是到达我们定义的HTTP处理方法前进行拦截, 其实, 如果在中间件中调用gin.ContextNext()方法, 则可以请求到达并完成业务处理后, 再经过中间件后置拦截处理.

在中间件调用Next()方法, Next()方法之前的代码会在到达请求方法前执行, Next()方法之后的代码则在请求方法处理后执行.

示例:

func MyMiddleware(c *gin.Context){
    c.Set("key",1000)
  c.Next()
  c.JSON(http.StatusOK,c.GetInt("key"))
}
router := gin.New()
router.GET("/test", MyMiddleware, func(c *gin.Context) {
  k := c.GetInt("key")
  c.Set("key", k+2000)
})
router.Run()

运行结果返回值为3000

同步响应和异步响应

每次request请求Gin会开启新的goroutine来处理请求. Gin同时支持请求的同步响应和异步响应. 示例如下:

func main() {
  // 1.创建路由
  // 默认使用了2个中间件Logger(), Recovery()
  r := gin.Default()
  // 1.异步
  r.GET("/long_async", func(c *gin.Context) {
    // 必须搞一个副本不能直接传递
    copyContext := c.Copy()
    c.JSON(http.StatusOK, "异步调用返回")
    // 异步处理
    go func() {
      log.Println("开始异步执行: " + copyContext.Request.URL.Path)
      time.Sleep(8 * time.Second)
      log.Println("结束异步执行: " + copyContext.Request.URL.Path)
    }()
  })
  
    // 2.同步
  r.GET("/long_sync", func(c *gin.Context) {
        log.Println("开始同步执行: " + c.Request.URL.Path)
    time.Sleep(5 * time.Second)
    log.Println("开始同步执行: " + c.Request.URL.Path)
    c.JSON(http.StatusOK, "同步调用返回")
  })
  r.Run(":8080")

}

关于父子goroutine 之间的关系可参考: 主goroutine结束, 子goroutine也立即结束?Goroutine理解 两篇Blog

Docker打包部署

待研究, 看了几个博客, 感觉Go的环境GOPATH, GOROOT啥的比较麻烦

  1. golang gin 服务器部署
  2. 部署go项目到k8s集群
  3. golang gin 项目部署 docker镜像制作 看这一篇就够了 项目部署一条龙

本文参考资料

  1. Gin官方文档