GIN 入门手册
简介
Gin 是一个用 Go 编写的 Web 框架. 在 Go 项目中使用通常会使用 package: 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 中非常非常重要的一个部分.
中间件的作用:
- Web 请求到到达我们定义的 HTTP 请求处理方法之前, 拦截请求并进行相应处理(比如: 权限验证, 数据过滤等), 这个可以类比为前置拦截器或前置过滤器.
- 在我们处理完成请求并响应客户端时, 拦截响应并进行相应的处理(比如: 添加统一响应部头或数据格式等), 这可以类比为后置拦截器或后置过滤器.
中间件实际上就是一个以 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)
拦截请求与后置拦截
拦截请求
比如我们有些请求需要用户登录或者需要特定权限才能访问, 这时候便可以中间件中做过滤拦截, 当用户请求不合法时, 可以使用下面列出的 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{})
后置拦截
前面我们讲的都是到达我们定义的 HTTP 处理方法前进行拦截, 其实, 如果在中间件中调用 gin.Context
的 Next()
方法, 则可以请求到达并完成业务处理后, 再经过中间件后置拦截处理.
在中间件调用 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 理解 两篇博客.
Docker 打包部署
本文参考资料
变更历史
最后更新于: 查看全部变更历史
docs: update docs
于 2024/11/25docs: update docs
于 2024/11/19更改navbar
于 2024/11/4整理文章代码格式
于 2024/11/1升级主题+整理文章格式
于 2024/11/1update
于 2024/10/17新增文字+CRLF全部替换为LF
于 2024/10/17升级主题+规整文章格式
于 2024/10/15升级主题+新增文章+修改格式
于 2024/10/14升级版本+规整文档中的格式
于 2024/10/11给予文件夹顺序
于 2024/9/24调整博客分类+修改about-me.md
于 2024/9/24first commit
于 2024/9/20