Gin — 高性能 Web 框架
Gin 是 Go 最流行的 Web 框架,基于 httprouter,性能极高,API 设计简洁。
安装
bash
go get -u github.com/gin-gonic/gin快速开始
go
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
}路由
go
r := gin.New()
// HTTP 方法
r.GET("/users", listUsers)
r.POST("/users", createUser)
r.PUT("/users/:id", updateUser)
r.PATCH("/users/:id", patchUser)
r.DELETE("/users/:id", deleteUser)
// 路径参数
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"id": id})
})
// 查询参数
r.GET("/search", func(c *gin.Context) {
q := c.Query("q")
page := c.DefaultQuery("page", "1")
c.JSON(200, gin.H{"q": q, "page": page})
})
// 路由组
v1 := r.Group("/api/v1")
{
v1.GET("/users", listUsers)
v1.POST("/users", createUser)
// 嵌套路由组
admin := v1.Group("/admin")
admin.Use(adminAuthMiddleware())
{
admin.GET("/stats", getStats)
}
}请求绑定
go
// 请求体结构体
type CreateUserRequest 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=0,lte=150"`
Password string `json:"password" binding:"required,min=8"`
}
func createUser(c *gin.Context) {
var req CreateUserRequest
// ShouldBindJSON:失败返回错误,不自动响应
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// 处理业务逻辑
user, err := userService.Create(c.Request.Context(), req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, user)
}
// 绑定查询参数
type ListUsersQuery struct {
Page int `form:"page" binding:"min=1"`
PageSize int `form:"pageSize" binding:"min=1,max=100"`
Search string `form:"search"`
}
func listUsers(c *gin.Context) {
var query ListUsersQuery
query.Page = 1
query.PageSize = 20
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// ...
}
// 绑定路径参数
type UserURI struct {
ID uint `uri:"id" binding:"required"`
}
func getUser(c *gin.Context) {
var uri UserURI
if err := c.ShouldBindUri(&uri); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// ...
}中间件
go
// 自定义中间件
func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
c.Next() // 执行后续处理器
latency := time.Since(start)
status := c.Writer.Status()
log.Printf("[%d] %s %s %v",
status, c.Request.Method, path, latency)
}
}
// JWT 认证中间件
func JWTAuth(secret string) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "未提供 token"})
return
}
claims, err := parseJWT(token, secret)
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "无效 token"})
return
}
// 将用户信息存入 context
c.Set("userID", claims.UserID)
c.Set("userRole", claims.Role)
c.Next()
}
}
// 限流中间件
func RateLimit(rps int) gin.HandlerFunc {
limiter := rate.NewLimiter(rate.Limit(rps), rps)
return func(c *gin.Context) {
if !limiter.Allow() {
c.AbortWithStatusJSON(429, gin.H{"error": "请求过于频繁"})
return
}
c.Next()
}
}
// 使用中间件
r := gin.New()
r.Use(gin.Recovery())
r.Use(RequestLogger())
r.Use(cors.Default())
api := r.Group("/api")
api.Use(JWTAuth(config.JWTSecret))
api.Use(RateLimit(100))响应
go
func handler(c *gin.Context) {
// JSON 响应
c.JSON(200, gin.H{"key": "value"})
// 带状态码的 JSON
c.JSON(http.StatusCreated, user)
// 字符串响应
c.String(200, "Hello, %s!", name)
// HTML 响应
c.HTML(200, "index.html", gin.H{"title": "首页"})
// 文件下载
c.File("./files/report.pdf")
c.FileAttachment("./files/report.pdf", "报告.pdf")
// 流式响应(SSE)
c.Stream(func(w io.Writer) bool {
if msg, ok := <-msgChan; ok {
c.SSEvent("message", msg)
return true
}
return false
})
// 重定向
c.Redirect(http.StatusMovedPermanently, "https://example.com")
}统一错误处理
go
// 自定义错误类型
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
}
func (e *APIError) Error() string { return e.Message }
// 错误处理中间件
func ErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) == 0 {
return
}
err := c.Errors.Last().Err
var apiErr *APIError
if errors.As(err, &apiErr) {
c.JSON(apiErr.Code, apiErr)
return
}
c.JSON(500, gin.H{"error": "服务器内部错误"})
}
}
// 在处理函数中使用
func getUser(c *gin.Context) {
user, err := userService.GetByID(c.Request.Context(), id)
if err != nil {
c.Error(err) // 传递给错误处理中间件
return
}
c.JSON(200, user)
}完整项目结构
myapp/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── handler/ # Gin 处理函数
│ │ ├── user.go
│ │ └── auth.go
│ ├── middleware/ # 中间件
│ │ ├── auth.go
│ │ └── logger.go
│ ├── service/ # 业务逻辑
│ ├── repository/ # 数据访问
│ └── model/ # 数据模型
├── pkg/
│ └── response/ # 统一响应格式
├── config/
└── docs/ # Swagger 文档go
// pkg/response/response.go — 统一响应格式
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data,omitempty"`
}
func Success(c *gin.Context, data any) {
c.JSON(200, Response{Code: 0, Message: "success", Data: data})
}
func Fail(c *gin.Context, code int, msg string) {
c.JSON(200, Response{Code: code, Message: msg})
}性能提示
- 生产环境设置
gin.SetMode(gin.ReleaseMode) - 使用
c.ShouldBind而非c.Bind(后者会自动写 400 响应) - 路由参数用
:id,通配符用*path