This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hzh/GIN/3-middleware/gin-vs-std.md
T

3.4 KiB
Raw Blame History

tags, create time
tags create time
后端
Go
Gin
中间件
net/http
2026-04-27 13:00

Gin vs net/http 中间件模式对比

概述

对比 Gin 中间件与 Go 标准库 net/http 装饰器模式的本质区别,分析各自在简洁性和灵活性上的优劣。

正文

1. 类型签名差异

标准库模式: func(http.Handler) http.Handler

  • 中间件接收下一个 handler,返回新的 handler
  • 本质是装饰器模式(Decorator Pattern),层层嵌套
  • 是函数式组合:middleware3(middleware2(middleware1(handler)))

Gin 模式: func(*gin.Context)

  • 中间件接收 *gin.Context,通过 c.Next() 主动推进到下一个
  • 本质是责任链模式(Chain of Responsibility),串联执行
  • 是命令式链式调用:m1 → m2 → m3 → handler
flowchart LR
    subgraph stdlib["标准库:装饰器嵌套"]
        S1["middleware3"] --> S2["middleware2"] --> S3["middleware1"] --> S4["handler"]
    end
    subgraph gin["Gin:责任链推进"]
        G1["m1"] --> G2["c.Next()"] --> G3["m2"] --> G4["c.Next()"] --> G5["handler"]
    end

    style stdlib fill:#F0F0F0
    style gin fill:#F0F0F0

2. 执行控制权

标准库模式中,中间件完全控制是否调用下一个 handler,通过闭包嵌套实现:

// 标准库:闭包嵌套,控制权在闭包内
func logging(next http.Handler) http.Handler {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()  // 前置
        next.ServeHTTP(w, r) // 推进(可选择不调用)
        // 后置
    }
}
// 必须显式嵌套:h := logging(auth(cors(handler)))

Gin 模式中,中间件显式调用 c.Next() 推进,写法是线性的:

// Gin:线性写法,c.Next() 就是推进
func logging(c *gin.Context) {
    start := time.Now()  // 前置
    c.Next()             // 推进
    // 后置
}
// 注册即可:r.Use(logging, auth, cors)

3. 各自优缺点

维度 net/http 装饰器模式 Gin 责任链模式
简洁性 嵌套深时阅读困难(括号地狱) 线性注册,一目了然
灵活性 高:可以完全跳过 next、包装 ResponseWriter/Request 中:依赖 c.Context 传递状态,c.Abort() 中断链
状态传递 靠 context.Context(类型安全) 靠 c.Keys(interface{},方便但类型不安全)
可观测性 需要自己包装 ResponseWriter 才能读状态码 c.Writer.Status() 直接获取
框架耦合 无,纯标准库,可跨框架复用 强耦合 Gin 的 *gin.Context
函数式风格 天然支持组合/高阶函数 命令式,更像过滤器链

4. 结论

Gin 的方案更简单,标准库的方案更灵活。

  • 简单性上 Gin 胜出:线性 Use() 注册 + c.Next() 推进,不用写嵌套闭包,新人上手快。
  • 灵活性上 标准库胜出:你可以用 http.RoundTripper、http.Handler 包装任意层,甚至写中间件组合子。Gin 被绑定在 *gin.Context 上,跨框架复用困难。

实际建议:如果在 Gin 生态内,用 Gin 中间件就够了。如果需要写可复用的中间件库(同时支持 Gin、Echo、标准库),应该写标准库风格的 func(http.Handler) http.Handler,然后用适配层桥接到各框架。

关联笔记