94 lines
2.8 KiB
Markdown
94 lines
2.8 KiB
Markdown
|
|
---
|
||
|
|
tags: [后端, Go, Gin, 中间件, 作用域]
|
||
|
|
create time: 2026-04-27 12:55
|
||
|
|
---
|
||
|
|
|
||
|
|
# 跨分组共享中间件的作用域选择
|
||
|
|
|
||
|
|
## 概述
|
||
|
|
|
||
|
|
讨论当多个 RouterGroup(如 `/api/v1` 和 `/api/v2`)需要同一中间件时,是注册到全局还是各自分组的取舍。结论:**同策略 → 全局;异策略 → 分组**。
|
||
|
|
|
||
|
|
## 正文
|
||
|
|
|
||
|
|
### 场景还原
|
||
|
|
|
||
|
|
```go
|
||
|
|
v1 := r.Group("/api/v1") // 需要 CORS
|
||
|
|
v2 := r.Group("/api/v2") // 也需要 CORS
|
||
|
|
```
|
||
|
|
|
||
|
|
两种方案:
|
||
|
|
|
||
|
|
**方案 A — 全局注册**(推荐 ✅)
|
||
|
|
|
||
|
|
```go
|
||
|
|
r.Use(cors()) // 一次注册,所有路由自动继承
|
||
|
|
|
||
|
|
v1 := r.Group("/api/v1")
|
||
|
|
v2 := r.Group("/api/v2")
|
||
|
|
```
|
||
|
|
|
||
|
|
**方案 B — 分组注册**
|
||
|
|
|
||
|
|
```go
|
||
|
|
v1 := r.Group("/api/v1", cors()) // 每个分组都写一遍
|
||
|
|
v2 := r.Group("/api/v2", cors()) // ← 重复代码
|
||
|
|
```
|
||
|
|
|
||
|
|
### 为什么全局更好?
|
||
|
|
|
||
|
|
**1. DRY — 避免重复**
|
||
|
|
|
||
|
|
分组注册需要在每个 Group 构造函数中手动传入中间件,新增版本时容易漏写。
|
||
|
|
|
||
|
|
**2. 不会遗漏 — 更安全**
|
||
|
|
|
||
|
|
```go
|
||
|
|
// 假设后来加了 v3
|
||
|
|
v3 := r.Group("/api/v3") // ← 分组注册下很容易忘记加 cors()
|
||
|
|
// 跨域请求静默失败,排查成本高
|
||
|
|
```
|
||
|
|
|
||
|
|
全局注册一劳永逸,永远不会遗漏。
|
||
|
|
|
||
|
|
**3. OPTIONS 预检拦截天然正确**
|
||
|
|
|
||
|
|
CORS 的核心逻辑是在最外层处理 `OPTIONS` 预检请求——预检触发条件、两轮对话机制、Header 语义详见 [[BACKEND/GIN/3-middleware/cors-registration-scope/cors-preflight|cors-preflight]]。
|
||
|
|
|
||
|
|
放在全局中间件中,预检请求在最早阶段被拦截,不会消耗后续中间件的算力。
|
||
|
|
|
||
|
|
**4. 性能差异可忽略**
|
||
|
|
|
||
|
|
Gin 的 `c.Next()` 只是切片遍历,一个 CORS 中间件多走一趟链的成本微乎其微。为了省这点开销拆分到分组里,得不偿失。
|
||
|
|
|
||
|
|
### 什么时候按分组注册?
|
||
|
|
|
||
|
|
只有一种情况例外:**不同分组需要不同的中间件配置**。
|
||
|
|
|
||
|
|
```go
|
||
|
|
// v1 宽松 — 允许所有来源
|
||
|
|
v1 := r.Group("/api/v1", corsAllowAll())
|
||
|
|
|
||
|
|
// v2 严格 — Origin 白名单校验
|
||
|
|
v2 := r.Group("/api/v2", corsStrict([]string{"https://app.example.com"}))
|
||
|
|
```
|
||
|
|
|
||
|
|
此时策略不同,自然不能复用同一个全局中间件。
|
||
|
|
|
||
|
|
### 决策对照表
|
||
|
|
|
||
|
|
| 条件 | 推荐方案 |
|
||
|
|
|------|---------|
|
||
|
|
| 各分组使用相同中间件配置 | **全局 `r.Use()`** |
|
||
|
|
| 各分组需要不同配置 | 各自分组注册 |
|
||
|
|
| 某些路由明确不需要该中间件 | 跳过全局,按需注册到子分组 |
|
||
|
|
| 中间件本身有副作用(如限流) | 评估后决定,可能需要分组隔离 |
|
||
|
|
|
||
|
|
## 关联笔记
|
||
|
|
|
||
|
|
- [[GIN/3-middleware/cors-preflight]] — OPTIONS 预检请求完整机制(触发条件、两轮对话、Max-Age 缓存)
|
||
|
|
- [[GIN/3-middleware]] — 中间件三级作用域机制
|
||
|
|
- [[middleware-abort]] — `c.Abort()` 终止机制
|
||
|
|
- [[GIN/gin-architecture]] — 中间件链底层执行机制
|