2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
2026-06-07 12:14:39 +08:00
|
|
|
|
tags: [go, golang, gin, Web框架, HTTP]
|
|
|
|
|
|
create time: 2026-06-07 15:20
|
2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# Gin
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 概述
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
Gin 是 Go 生态中最流行的 Web 框架,以高性能路由和简洁的 API 著称。本文从基础用法到中间件、分组路由,覆盖构建 RESTful API 的核心技能。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 正文
|
|
|
|
|
|
|
|
|
|
|
|
### 安装
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
go get -u github.com/gin-gonic/gin
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### 快速开始
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!question] 💭 思考
|
|
|
|
|
|
> 如果让你从零实现一个 HTTP 服务器,需要处理哪些事情?(路由匹配、请求解析、响应格式化……)
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
Gin 封装了这些底层细节,让你专注于业务逻辑:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r := gin.Default() // 创建默认引擎(含 Logger + Recovery 中间件)
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r.GET("/ping", func(c *gin.Context) {
|
|
|
|
|
|
c.JSON(200, gin.H{"message": "pong"})
|
|
|
|
|
|
})
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r.Run(":8080") // 监听 8080 端口
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!tip] 💡 Default vs New
|
|
|
|
|
|
> - `gin.New()`:纯引擎,无日志和恢复中间件
|
|
|
|
|
|
> - `gin.Default()`:预装 Logger 和 Recovery,生产环境推荐
|
|
|
|
|
|
> - Recovery 中间件会自动 recover panic,避免服务崩溃
|
|
|
|
|
|
|
|
|
|
|
|
### 路由与参数
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
#### 路径参数
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
r.GET("/user/:name", func(c *gin.Context) {
|
2026-06-07 12:14:39 +08:00
|
|
|
|
name := c.Param("name") // 获取 :name 的值
|
|
|
|
|
|
c.String(200, "Hello %s", name)
|
2026-06-07 11:08:10 +08:00
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
#### Query 参数
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r.GET("/search", func(c *gin.Context) {
|
|
|
|
|
|
q := c.Query("q", "") // 获取 ?q=xxx,默认值 ""
|
|
|
|
|
|
page := c.DefaultQuery("page", "1") // 带默认值
|
|
|
|
|
|
c.JSON(200, gin.H{"query": q, "page": page})
|
2026-06-07 11:08:10 +08:00
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
#### POST 表单 / JSON
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
type LoginRequest struct {
|
|
|
|
|
|
Username string `json:"username" binding:"required"`
|
|
|
|
|
|
Password string `json:"password" binding:"required"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
r.POST("/login", func(c *gin.Context) {
|
2026-06-07 12:14:39 +08:00
|
|
|
|
var req LoginRequest
|
|
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
|
|
|
c.JSON(400, gin.H{"error": err.Error()})
|
2026-06-07 11:08:10 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-06-07 12:14:39 +08:00
|
|
|
|
// 处理登录...
|
|
|
|
|
|
c.JSON(200, gin.H{"message": "ok"})
|
2026-06-07 11:08:10 +08:00
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!warning] ⚠️ 绑定注意事项
|
|
|
|
|
|
> - `ShouldBindJSON` 仅在 Content-Type 为 `application/json` 时生效
|
|
|
|
|
|
> - `binding:"required"` 由内置验证器检查字段是否为空
|
|
|
|
|
|
> - 对于复杂验证需求,建议使用 `github.com/go-playground/validator`
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
### 中间件
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!question] 💭 思考
|
|
|
|
|
|
> 如果你的 API 需要对所有请求做日志记录、鉴权、CORS 处理,在每个 handler 里重复写代码合理吗?
|
|
|
|
|
|
|
|
|
|
|
|
中间件是横切关注点的标准解决方案——它在请求到达 handler **之前**和返回响应**之后**执行:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
// 自定义中间件
|
|
|
|
|
|
func RequestID() gin.HandlerFunc {
|
2026-06-07 11:08:10 +08:00
|
|
|
|
return func(c *gin.Context) {
|
2026-06-07 12:14:39 +08:00
|
|
|
|
id := uuid.New().String()
|
|
|
|
|
|
c.Set("requestID", id) // 存入上下文
|
|
|
|
|
|
c.Header("X-Request-ID", id)
|
|
|
|
|
|
c.Next() // ✅ 必须调用,继续处理后续中间件/handler
|
|
|
|
|
|
// 这里可以记录耗时等后处理逻辑
|
2026-06-07 11:08:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
// 注册方式
|
|
|
|
|
|
r.Use(RequestID()) // 全局中间件
|
|
|
|
|
|
r.GET("/api", MyHandler) // 受中间件保护
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!info] ℹ️ 中间件执行顺序
|
|
|
|
|
|
> 先注册的先执行(类似洋葱模型):
|
|
|
|
|
|
> ```
|
|
|
|
|
|
> 请求 → Logger → Auth → CORS → Handler
|
|
|
|
|
|
> 响应 ← Logger ← Auth ← CORS ← Handler
|
|
|
|
|
|
> ```
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
#### 分组路由
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
2026-06-07 12:14:39 +08:00
|
|
|
|
v1 := r.Group("/api/v1")
|
2026-06-07 11:08:10 +08:00
|
|
|
|
{
|
2026-06-07 12:14:39 +08:00
|
|
|
|
v1.GET("/users", getUsers)
|
|
|
|
|
|
v1.POST("/users", createUser)
|
|
|
|
|
|
v1.PUT("/users/:id", updateUser)
|
|
|
|
|
|
v1.DELETE("/users/:id", deleteUser)
|
2026-06-07 11:08:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
// 为组注册中间件
|
|
|
|
|
|
authGroup := r.Group("/api", AuthMiddleware())
|
2026-06-07 11:08:10 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### RESTful 完整示例
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
type User struct {
|
|
|
|
|
|
ID int `json:"id"`
|
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
|
Age int `json:"age"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
var users []User
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
|
r := gin.Default()
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r.GET("/users", func(c *gin.Context) {
|
|
|
|
|
|
c.JSON(200, users)
|
|
|
|
|
|
})
|
|
|
|
|
|
r.GET("/users/:id", func(c *gin.Context) {
|
|
|
|
|
|
id, _ := strconv.Atoi(c.Param("id"))
|
|
|
|
|
|
for _, u := range users {
|
|
|
|
|
|
if u.ID == id {
|
|
|
|
|
|
c.JSON(200, u)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-06-07 11:08:10 +08:00
|
|
|
|
}
|
2026-06-07 12:14:39 +08:00
|
|
|
|
c.JSON(404, gin.H{"error": "not found"})
|
|
|
|
|
|
})
|
|
|
|
|
|
r.POST("/users", func(c *gin.Context) {
|
|
|
|
|
|
var u User
|
|
|
|
|
|
c.ShouldBindJSON(&u)
|
|
|
|
|
|
u.ID = len(users) + 1
|
|
|
|
|
|
users = append(users, u)
|
|
|
|
|
|
c.JSON(201, u)
|
|
|
|
|
|
})
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
r.Run(":8080")
|
2026-06-07 11:08:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!tip] 💡 性能提示
|
|
|
|
|
|
> - `gin.H` 是 `map[string]interface{}` 的别名,在高频场景中考虑使用自定义结构体替代,减少反射开销
|
|
|
|
|
|
> - 启用 Gzip 压缩:`r.Use(gzip.Gzip(gzip.DefaultCompression))`
|
|
|
|
|
|
> - 生产环境建议关闭 Debug 模式:`gin.SetMode(gin.ReleaseMode)`
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 关联笔记
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
- [[hzh/GolangStar/Go语言框架/gorm]] — Gin + GORM 是全栈开发的经典组合
|
|
|
|
|
|
- [[hzh/GolangStar/Go语言进阶/Context]] — HTTP 请求天然适合用 context 控制超时和取消
|