Gin是Go语言最流行的Web框架,基于httprouter,性能优异,API简洁。本文从安装到中间件,覆盖Gin的核心用法。
安装与Hello World
go get -u github.com/gin-gonic/gin
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default() // 包含Logger和Recovery中间件
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.Run(":8080") // 默认监听 0.0.0.0:8080
}
gin.H是map[string]interface{}的快捷方式。
路由与HTTP方法
func main() {
r := gin.Default()
r.GET("/users", listUsers)
r.GET("/users/:id", getUser)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.DELETE("/users/:id", deleteUser)
r.Run(":8080")
}
路径参数
// GET /users/42
func getUser(c *gin.Context) {
id := c.Param("id")
c.JSON(http.StatusOK, gin.H{"id": id})
}
查询参数
// GET /users?page=1&size=20&name=alice
func listUsers(c *gin.Context) {
page := c.DefaultQuery("page", "1")
size := c.DefaultQuery("size", "10")
name := c.Query("name") // 没有则返回空字符串
c.JSON(http.StatusOK, gin.H{
"page": page,
"size": size,
"name": name,
})
}
参数绑定
用结构体标签自动解析和验证请求参数:
type CreateUserReq struct {
Name string `json:"name" binding:"required,min=2,max=50"`
Email string `json:"email" binding:"required,email"`
Age int `json:"age" binding:"required,gte=1,lte=150"`
Password string `json:"password" binding:"required,min=6"`
}
func createUser(c *gin.Context) {
var req CreateUserReq
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// req已通过验证,可以使用
c.JSON(http.StatusCreated, gin.H{
"message": "user created",
"name": req.Name,
})
}
ShouldBindJSON从请求体解析JSON并校验。类似的还有ShouldBindQuery(查询参数)、ShouldBind(根据Content-Type自动选择)。
中间件
内置中间件
r := gin.New() // 空白引擎,不带任何中间件
// 全局中间件
r.Use(gin.Logger()) // 请求日志
r.Use(gin.Recovery()) // panic恢复,返回500而不是crash
自定义中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing token"})
c.Abort() // 终止后续处理
return
}
// 验证token...
userID, err := validateToken(token)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
c.Abort()
return
}
// 将用户信息存入上下文,后续handler可以读取
c.Set("userID", userID)
c.Next() // 继续执行后续handler
}
}
请求耗时中间件
func TimingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
log.Printf("[%s] %s - %d - %v",
c.Request.Method,
c.Request.URL.Path,
c.Writer.Status(),
duration,
)
}
}
分组路由
func main() {
r := gin.Default()
// 公开接口
public := r.Group("/api/v1")
{
public.POST("/login", login)
public.POST("/negister", register)
}
// 需要认证的接口
auth := r.Group("/api/v1")
auth.Use(AuthMiddleware())
{
auth.GET("/profile", getProfile)
auth.PUT("/profile", updateProfile)
auth.GET("/orders", listOrders)
auth.POST("/orders", createOrder)
}
// 管理员接口
admin := r.Group("/api/v1/admin")
admin.Use(AuthMiddleware(), AdminOnly())
{
admin.GET("/users", adminListUsers)
admin.DELETE("/users/:id", adminDeleteUser)
}
r.Run(":8080")
}
分组路由的好处:共享前缀路径和中间件,代码结构清晰。
JSON响应
// 结构体响应
type UserResponse struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
func getUser(c *gin.Context) {
user := UserResponse{
ID: 1,
Name: "Alice",
Email: "alice@example.com",
CreatedAt: time.Now(),
}
c.JSON(http.StatusOK, user)
}
// 统一响应格式
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, Response{
Code: 0,
Message: "success",
Data: data,
})
}
func Error(c *gin.Context, code int, message string) {
c.JSON(http.StatusOK, Response{
Code: code,
Message: message,
})
}
项目结构建议
myapp/
├── main.go # 入口
├── router/
│ └── router.go # 路由注册
├── handler/ # 请求处理
│ ├── user.go
│ └── order.go
├── middleware/ # 中间件
│ └── auth.go
├── model/ # 数据模型
│ └── user.go
├── service/ # 业务逻辑
│ └── user.go
└── config/ # 配置
└── config.go
Gin的哲学是轻量和快速,核心只做路由和中间件,ORM、配置管理、日志等按需搭配。这种灵活性正是Go社区推崇的风格。