Gin 与标准库对比

Go 的标准库 net/http 已经能处理 HTTP 请求了,为什么还需要 Gin?这一章我们来对比一下两者的区别。

标准库写法

先用标准库实现一个简单的 API:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

var users = []User{
    {ID: "1", Name: "Alice"},
    {ID: "2", Name: "Bob"},
}

func main() {
    http.HandleFunc("/users", usersHandler)
    http.HandleFunc("/users/", userHandler)

    log.Fatal(http.ListenAndServe(":8080", nil))
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    switch r.Method {
    case "GET":
        json.NewEncoder(w).Encode(users)
    case "POST":
        var user User
        json.NewDecoder(r.Body).Decode(&user)
        users = append(users, user)
        json.NewEncoder(w).Encode(user)
    default:
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    id := r.URL.Path[len("/users/"):]

    for _, user := range users {
        if user.ID == id {
            json.NewEncoder(w).Encode(user)
            return
        }
    }

    http.Error(w, "User not found", http.StatusNotFound)
}

Gin 写法

同样的功能用 Gin 实现:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

var users = []User{
    {ID: "1", Name: "Alice"},
    {ID: "2", Name: "Bob"},
}

func main() {
    r := gin.Default()

    r.GET("/users", getUsers)
    r.POST("/users", createUser)
    r.GET("/users/:id", getUser)

    r.Run(":8080")
}

func getUsers(c *gin.Context) {
    c.JSON(http.StatusOK, users)
}

func createUser(c *gin.Context) {
    var user User
    c.ShouldBindJSON(&user)
    users = append(users, user)
    c.JSON(http.StatusOK, user)
}

func getUser(c *gin.Context) {
    id := c.Param("id")

    for _, user := range users {
        if user.ID == id {
            c.JSON(http.StatusOK, user)
            return
        }
    }

    c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
}

主要区别

路由

标准库的路由功能比较基础,不支持路径参数:

http.HandleFunc("/users/", userHandler)

需要手动解析 URL 提取参数:

id := r.URL.Path[len("/users/"):]

Gin 支持路径参数,自动解析:

r.GET("/users/:id", getUser)

func getUser(c *gin.Context) {
    id := c.Param("id")
}

JSON 处理

标准库需要手动处理 JSON:

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)

Gin 一行搞定:

c.JSON(200, data)

请求绑定

标准库解析请求体:

var user User
json.NewDecoder(r.Body).Decode(&user)

Gin 支持绑定和验证:

var user User
if err := c.ShouldBindJSON(&user); err != nil {
    c.JSON(400, gin.H{"error": err.Error()})
    return
}

中间件

标准库没有内置中间件机制,需要自己实现:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("请求: %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

Gin 内置中间件支持:

r.Use(func(c *gin.Context) {
    log.Printf("请求: %s %s", c.Request.Method, c.Request.URL.Path)
    c.Next()
})

性能对比

Gin 的路由性能比标准库的 http.ServeMux 高很多。路由越多,差距越明显。

路由数量标准库 (ns/op)Gin (ns/op)
15040
1020045
100200050
10002500055

标准库的路由是线性查找,时间复杂度 O(n)。Gin 使用基数树,时间复杂度接近 O(1)。

什么时候用标准库

标准库适合:

  • 极简单的服务
  • 不想引入第三方依赖
  • 对二进制大小敏感

什么时候用 Gin

Gin 适合:

  • RESTful API 服务
  • 需要路由参数
  • 需要中间件
  • 项目会逐渐变大

小结

特性标准库Gin
路由参数不支持支持
JSON 响应手动内置
中间件手动实现内置支持
请求验证手动内置
路由性能O(n)O(1)
学习成本

如果你的项目只是简单的几个接口,标准库够用。但一旦项目变大,Gin 能帮你省很多事。