Files
cs-note/hhs/GIN/3-middleware/gin-vs-std.md
T
2026-05-24 11:42:38 +08:00

90 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [后端, Go, Gin, 中间件, net/http]
create time: 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`
```mermaid
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,通过闭包嵌套实现:
```go
// 标准库:闭包嵌套,控制权在闭包内
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()` 推进,写法是线性的:
```go
// 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`,然后用适配层桥接到各框架。
## 关联笔记
- [[GIN/3-middleware]] — 中间件完整机制