Go mod 好菜系列 - 0x01 Gin 真的是第一口常见菜
详细聊 gin 是什么、适合干什么、路由/中间件/参数绑定/响应处理怎么写,以及为什么它在 Go Web 项目里这么常见。
如果你在 Go 里开始写 Web 服务,大概率很快就会碰到 gin。它的存在感高到一种程度:很多人甚至不是先学标准库里的 net/http,而是先学 gin。这个顺序未必最理想,但它确实说明了 gin 的工程地位。
gin 是干嘛的
一句话说,就是一个 Web 框架。更准确一点说,它是在标准库 net/http 之上,给你补了一整套更顺手的接口开发工具:
- 路由组织
- 中间件
- 请求参数读取
- JSON 响应
- 参数绑定和校验衔接
如果你只用标准库,很多事情也不是做不到,只是你会写得更原始一些,重复代码也会更多一些。
为什么 gin 常见
因为它在很多团队里处在一个很舒服的位置:
- 比纯标准库省事
- 比一些更重的框架轻
- 文档和资料多
- 大家都认识,接手成本低
它不一定是唯一选择,但它经常是“大家都能接受、也比较容易上手”的那一个。
最小可用示例
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(":8080")
}这里有几个点一眼先记住:
gin.Default()会默认带上日志和 recover 中间件r.GET注册路由c.JSON直接返回 JSONr.Run启动服务
也就是说,很多你在标准库里要自己拼的东西,gin 已经给你包装得比较顺手了。
路由写起来为什么更舒服
标准库当然也能注册路由,但 gin 在路由层做得更直观,尤其是分组。
api := r.Group("/api")
{
api.GET("/users", listUsers)
api.GET("/users/:id", getUser)
api.POST("/users", createUser)
}这个写法在接口一多的时候很有用。路径前缀、鉴权中间件、版本区分,都更容易挂在 group 上统一处理。
参数获取和绑定
这是 gin 特别讨喜的一点。它把参数来源拆得很清楚:
- 路径参数:
c.Param() - 查询参数:
c.Query() - 表单参数:
c.PostForm() - JSON 绑定:
c.ShouldBindJSON()
func getUser(c *gin.Context) {
id := c.Param("id")
keyword := c.Query("keyword")
c.JSON(200, gin.H{
"id": id,
"keyword": keyword,
})
}再看 JSON 绑定:
type CreateUserReq struct {
Name string `json:"name" binding:"required"`
Age int `json:"age"`
}
func createUser(c *gin.Context) {
var req CreateUserReq
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"name": req.Name})
}这也是为什么 gin 特别适合写 API:它已经把请求绑定这类脏活累活做得很顺了。
中间件为什么是 gin 的重头戏
gin 项目里,中间件的存在感非常高。日志、鉴权、限流、panic 兜底、统一 request id,很多事情都会落到这里。
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
return
}
c.Next()
}
}api := r.Group("/api")
api.Use(AuthMiddleware())这套写法很适合工程里做横切逻辑。别把这些检查全塞到每个 handler 里,不然很快就会变成复制粘贴型开发。
gin 能帮你省什么事
- 少写很多请求解析和响应包装的样板代码
- 接口组织更清晰
- 中间件扩展方便
- 和 validator、JWT、日志这些工具库衔接比较顺
也就是说,gin 不是为了“更炫”,而是为了让接口开发更像一条流水线。
gin 不是万能的地方
也别把 gin 神化。它不是说用了以后项目结构就自动优雅了。很多 gin 项目也一样会烂,只是烂得更快。
常见坏味道包括:
- 所有业务都写在 handler 里
- 路由文件越来越大
- 绑定、鉴权、数据库、响应全糊在一起
- 把 gin.Context 一路传到 service 和 repository 里当万能口袋
gin 只是入口层框架,不是架构本身。你还是得自己把层次划清。
适合放在项目哪一层
最健康的理解是:gin 属于 接口入口层。它负责:
- 收请求
- 做初步绑定和校验
- 调用 service
- 把结果编码成 HTTP 响应
数据库操作、核心业务逻辑、复杂规则判断,别因为 gin 好用,就顺手全写在 c 这个变量边上。
小结
gin 之所以常见,不是因为它神,而是因为它刚好把 Go Web 里那层最烦人的样板劳动解决掉了:
- 它让路由、中间件、参数绑定和 JSON 响应更顺手
- 它特别适合接口型服务
- 它不负责替你设计分层,只负责让入口层更舒服
- 会用 gin 很重要,但别把整个项目都写成 gin 脚本
下一篇我们聊 gorm。gin 解决的是入口层,那 gorm 解决的,就是另一块特别高频又特别容易写烦的地方:数据库。