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 实现:
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:
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) |
|---|---|---|
| 1 | 50 | 40 |
| 10 | 200 | 45 |
| 100 | 2000 | 50 |
| 1000 | 25000 | 55 |
标准库的路由是线性查找,时间复杂度 O(n)。Gin 使用基数树,时间复杂度接近 O(1)。
标准库适合:
Gin 适合:
| 特性 | 标准库 | Gin |
|---|---|---|
| 路由参数 | 不支持 | 支持 |
| JSON 响应 | 手动 | 内置 |
| 中间件 | 手动实现 | 内置支持 |
| 请求验证 | 手动 | 内置 |
| 路由性能 | O(n) | O(1) |
| 学习成本 | 低 | 低 |
如果你的项目只是简单的几个接口,标准库够用。但一旦项目变大,Gin 能帮你省很多事。