Files
go-common/docs/middleware.md

1040 lines
27 KiB
Markdown
Raw Permalink 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.

# 中间件工具文档
## 概述
中间件工具提供了常用的HTTP中间件功能包括CORS处理、时区管理、请求日志、Panic恢复和限流等。
## 功能特性
- **CORS中间件**:支持跨域资源共享配置
- **时区中间件**:从请求头读取时区信息,支持默认时区设置
- **日志中间件**自动记录每个HTTP请求的详细信息
- **Recovery中间件**捕获panic并恢复防止服务崩溃
- **限流中间件**:基于令牌桶算法的请求限流
- **中间件链**:提供便捷的中间件链式调用
## CORS中间件
### 功能说明
CORS中间件用于处理跨域资源共享支持
- 配置允许的源(支持通配符)
- 配置允许的HTTP方法
- 配置允许的请求头
- 配置暴露的响应头
- 支持凭证传递
- 预检请求缓存时间设置
### 使用方法
#### 基本使用(默认配置)
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 处理请求
})
// 使用默认CORS配置
corsHandler := middleware.CORS()(handler)
http.Handle("/api", corsHandler)
http.ListenAndServe(":8080", nil)
}
```
#### 自定义配置
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
// 自定义CORS配置
corsConfig := &middleware.CORSConfig{
AllowedOrigins: []string{
"https://example.com",
"https://app.example.com",
"*.example.com", // 支持通配符
},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{
"Content-Type",
"Authorization",
"X-Requested-With",
"X-Timezone",
},
ExposedHeaders: []string{"X-Total-Count"},
AllowCredentials: true,
MaxAge: 3600, // 1小时
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 处理请求
})
corsHandler := middleware.CORS(corsConfig)(handler)
http.Handle("/api", corsHandler)
http.ListenAndServe(":8080", nil)
}
```
#### 允许所有源(开发环境)
```go
corsConfig := &middleware.CORSConfig{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"*"},
}
corsHandler := middleware.CORS(corsConfig)(handler)
```
### CORSConfig 配置说明
| 字段 | 类型 | 说明 | 默认值 |
|------|------|------|--------|
| AllowedOrigins | []string | 允许的源,支持 "*" 和 "*.example.com" | ["*"] |
| AllowedMethods | []string | 允许的HTTP方法 | ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"] |
| AllowedHeaders | []string | 允许的请求头 | ["Content-Type", "Authorization", "X-Requested-With", "X-Timezone"] |
| ExposedHeaders | []string | 暴露给客户端的响应头 | [] |
| AllowCredentials | bool | 是否允许发送凭证 | false |
| MaxAge | int | 预检请求缓存时间(秒) | 86400 |
### 注意事项
1. 如果 `AllowCredentials``true``AllowedOrigins` 不能使用 "*",必须指定具体的源
2. 通配符支持:`"*.example.com"` 会匹配 `"https://app.example.com"` 等子域名
3. 预检请求OPTIONS会自动处理无需在业务代码中处理
## 时区中间件
### 功能说明
时区中间件用于从请求头读取时区信息并存储到context中方便后续使用。
- 从请求头 `X-Timezone` 读取时区
- 如果未传递时区信息,使用默认时区 `AsiaShanghai`
- 时区信息存储到context中可通过Handler的`GetTimezone()`方法获取
- 自动验证时区有效性,无效时区会回退到默认时区
### 使用方法
#### 基本使用(默认时区 AsiaShanghai
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func handler(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
// 从Handler获取时区
timezone := h.GetTimezone()
// 使用时区
now := tools.Now(timezone)
tools.FormatDateTime(now, timezone)
h.Success(map[string]interface{}{
"timezone": timezone,
"time": tools.FormatDateTime(now),
})
}
func main() {
handler := middleware.Timezone(http.HandlerFunc(handler))
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 自定义默认时区
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
"git.toowon.com/jimmy/go-common/tools"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 处理请求
}
func main() {
// 使用自定义默认时区
handler := middleware.TimezoneWithDefault(tools.UTC)(http.HandlerFunc(handler))
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 在业务代码中使用时区
**推荐方式:通过 factory 使用(黑盒模式)**
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/factory"
)
func GetUserList(w http.ResponseWriter, r *http.Request) {
fac, _ := factory.NewFactoryFromFile("config.json")
// 从请求中获取时区
timezone := fac.GetTimezone(r)
// 使用时区进行时间处理
now := fac.Now(timezone)
// 查询数据时使用时区
startTime := fac.StartOfDay(now, timezone)
endTime := fac.EndOfDay(now, timezone)
// 返回数据
fac.Success(w, map[string]interface{}{
"timezone": timezone,
"startTime": fac.FormatDateTime(startTime),
"endTime": fac.FormatDateTime(endTime),
})
}
```
**或者直接使用 tools 包:**
```go
import (
"net/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func GetUserList(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
// 从Handler获取时区
timezone := h.GetTimezone()
// 使用时区进行时间处理
now := tools.Now(timezone)
// 查询数据时使用时区
startTime := tools.StartOfDay(now, timezone)
endTime := tools.EndOfDay(now, timezone)
// 返回数据
h.Success(map[string]interface{}{
"timezone": timezone,
"startTime": tools.FormatDateTime(startTime),
"endTime": tools.FormatDateTime(endTime),
})
}
```
### 请求头格式
客户端需要在请求头中传递时区信息:
```
X-Timezone: Asia/Shanghai
```
支持的时区格式IANA时区数据库
- `Asia/Shanghai`
- `America/New_York`
- `Europe/London`
- `UTC`
- 等等
### 注意事项
1. 如果请求头中未传递 `X-Timezone`,默认使用 `AsiaShanghai`
2. 如果传递的时区无效,会自动回退到默认时区
3. 时区信息存储在context中可以在整个请求生命周期中使用
4. 建议在CORS配置中包含 `X-Timezone` 请求头
## 日志中间件
### 功能说明
日志中间件用于自动记录每个HTTP请求的详细信息帮助监控和调试应用。
记录内容包括:
- 请求方法、路径、查询参数
- 响应状态码、响应大小
- 请求处理时间(毫秒)
- 客户端IP地址支持X-Forwarded-For
- User-Agent、Referer等信息
### 使用方法
#### 基本使用使用默认logger
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
chain := middleware.NewChain(
middleware.Logging(nil), // 使用默认配置
middleware.CORS(),
middleware.Timezone,
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 使用自定义logger
```go
import (
"git.toowon.com/jimmy/go-common/middleware"
"git.toowon.com/jimmy/go-common/logger"
)
func main() {
// 创建自定义logger异步模式输出到文件
loggerConfig := &logger.LoggerConfig{
Level: "info",
Output: "file",
FilePath: "./logs/app.log",
Async: true, // 异步模式,不阻塞请求
BufferSize: 1000,
}
myLogger, _ := logger.NewLogger(loggerConfig)
// 配置日志中间件
loggingConfig := &middleware.LoggingConfig{
Logger: myLogger,
SkipPaths: []string{"/health", "/metrics"}, // 跳过健康检查接口
}
chain := middleware.NewChain(
middleware.Logging(loggingConfig),
middleware.CORS(),
middleware.Timezone,
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
### LoggingConfig 配置说明
| 字段 | 类型 | 说明 | 默认值 |
|------|------|------|--------|
| Logger | *logger.Logger | 日志记录器 | 创建默认loggerstdout |
| SkipPaths | []string | 跳过记录的路径列表 | [] |
| LogRequestBody | bool | 是否记录请求体(慎用) | false |
| LogResponseBody | bool | 是否记录响应体(慎用) | false |
### 日志输出示例
```
[INFO] HTTP Request method=GET path=/api/users query= status=200 size=1024 duration=45 ip=192.168.1.100 user_agent=Mozilla/5.0 referer=
[WARN] HTTP Request method=POST path=/api/users query= status=400 size=128 duration=12 ip=192.168.1.100 user_agent=PostmanRuntime/7.29
[ERROR] HTTP Request method=GET path=/api/error query= status=500 size=256 duration=89 ip=192.168.1.100 user_agent=curl/7.64.1 referer=
```
### 注意事项
1. **异步模式推荐**生产环境建议使用异步logger避免日志写入阻塞请求
2. **跳过路径**:健康检查、监控接口等高频接口建议跳过日志记录
3. **日志级别**根据状态码自动选择日志级别5xx=ERROR, 4xx=WARN, 2xx-3xx=INFO
4. **客户端IP**自动从X-Forwarded-For、X-Real-IP或RemoteAddr获取真实IP
## Recovery中间件
### 功能说明
Recovery中间件用于捕获HTTP处理过程中的panic防止panic导致整个服务崩溃。
功能包括:
- 捕获panic并恢复服务
- 记录panic信息和堆栈跟踪
- 返回500错误响应
- 支持自定义错误处理
### 使用方法
#### 基本使用(使用默认配置)
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
chain := middleware.NewChain(
middleware.Recovery(nil), // 使用默认配置
middleware.Logging(nil),
middleware.CORS(),
middleware.Timezone,
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 使用自定义logger
```go
import (
"git.toowon.com/jimmy/go-common/middleware"
"git.toowon.com/jimmy/go-common/logger"
)
func main() {
myLogger, _ := logger.NewLogger(nil)
recoveryConfig := &middleware.RecoveryConfig{
Logger: myLogger,
EnableStackTrace: true, // 启用堆栈跟踪
}
chain := middleware.NewChain(
middleware.Recovery(recoveryConfig),
middleware.Logging(nil),
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 自定义错误响应
```go
import (
"net/http"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
recoveryConfig := &middleware.RecoveryConfig{
EnableStackTrace: true,
CustomHandler: func(w http.ResponseWriter, r *http.Request, err interface{}) {
// 使用统一的JSON响应格式
h := commonhttp.NewHandler(w, r)
h.SystemError("服务器内部错误")
},
}
chain := middleware.NewChain(
middleware.Recovery(recoveryConfig),
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
### RecoveryConfig 配置说明
| 字段 | 类型 | 说明 | 默认值 |
|------|------|------|--------|
| Logger | *logger.Logger | 日志记录器 | 创建默认logger |
| EnableStackTrace | bool | 是否记录堆栈跟踪 | true |
| CustomHandler | func(...) | 自定义错误处理函数 | nil返回500文本 |
### 注意事项
1. **放在最外层**Recovery中间件应该放在中间件链的最前面以捕获所有panic
2. **日志记录**建议配置logger确保panic信息被记录下来
3. **堆栈跟踪**:生产环境建议启用,方便排查问题
4. **自定义响应**:可以自定义错误响应格式,统一错误处理
## 限流中间件
### 功能说明
限流中间件用于限制请求频率防止API被滥用或遭受攻击。
特性:
- 基于令牌桶算法
- 支持按IP、用户ID等维度限流
- 自动设置限流响应头
- 内存存储,自动清理过期数据
### 使用方法
#### 基本使用默认配置100请求/分钟)
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
chain := middleware.NewChain(
middleware.Recovery(nil),
middleware.Logging(nil),
middleware.RateLimit(nil), // 默认100请求/分钟按IP限流
middleware.CORS(),
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 自定义限流规则
```go
import (
"time"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
// 创建限流器10请求/分钟
limiter := middleware.NewTokenBucketLimiter(10, time.Minute)
rateLimitConfig := &middleware.RateLimitConfig{
Limiter: limiter,
}
chain := middleware.NewChain(
middleware.RateLimit(rateLimitConfig),
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 按用户ID限流
```go
import (
"net/http"
"time"
"git.toowon.com/jimmy/go-common/middleware"
)
func main() {
limiter := middleware.NewTokenBucketLimiter(100, time.Minute)
rateLimitConfig := &middleware.RateLimitConfig{
Limiter: limiter,
KeyFunc: func(r *http.Request) string {
// 从请求头或JWT token中获取用户ID
userID := r.Header.Get("X-User-ID")
if userID != "" {
return "user:" + userID
}
// 如果没有用户ID使用IP
return "ip:" + r.RemoteAddr
},
OnRateLimitExceeded: func(w http.ResponseWriter, r *http.Request, key string) {
// 记录限流事件
println("Rate limit exceeded for:", key)
},
}
chain := middleware.NewChain(
middleware.RateLimit(rateLimitConfig),
)
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 便捷函数
```go
// 按IP限流10请求/分钟
chain := middleware.NewChain(
middleware.RateLimitByIP(10, time.Minute),
)
// 或使用自定义速率
chain := middleware.NewChain(
middleware.RateLimitWithRate(50, time.Minute),
)
```
### RateLimitConfig 配置说明
| 字段 | 类型 | 说明 | 默认值 |
|------|------|------|--------|
| Limiter | RateLimiter | 限流器实例 | 100请求/分钟 |
| KeyFunc | func(*http.Request) string | 生成限流键的函数 | 使用客户端IP |
| OnRateLimitExceeded | func(...) | 限流触发回调 | nil |
### 响应头说明
限流中间件会自动设置以下响应头:
| 响应头 | 说明 |
|--------|------|
| X-RateLimit-Limit | 窗口期内允许的请求数 |
| X-RateLimit-Remaining | 当前窗口剩余配额 |
| X-RateLimit-Reset | 配额重置时间Unix时间戳 |
| Retry-After | 限流时,建议重试的等待时间(秒) |
### 响应示例
正常请求:
```
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000
```
触发限流:
```
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
Retry-After: 45
Too Many Requests
```
### 注意事项
1. **内存存储**当前实现使用内存存储适用于单机部署。如需分布式限流建议使用Redis
2. **键设计**合理设计限流键可以按IP、用户、API等维度限流
3. **清理机制**:自动清理过期的限流数据,避免内存泄漏
4. **算法选择**:使用令牌桶算法,支持突发流量
## 中间件链
### 功能说明
中间件链提供便捷的中间件组合方式,支持链式调用。
### 使用方法
```go
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 处理请求
}
func main() {
// 创建中间件链
chain := middleware.NewChain(
middleware.CORS(),
middleware.Timezone,
)
// 应用到处理器
handler := chain.ThenFunc(handler)
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 链式追加中间件
```go
chain := middleware.NewChain(middleware.CORS())
chain.Append(middleware.Timezone)
handler := chain.ThenFunc(handler)
```
## 完整示例
### 示例1完整的生产级中间件配置
```go
package main
import (
"log"
"net/http"
"time"
"git.toowon.com/jimmy/go-common/middleware"
"git.toowon.com/jimmy/go-common/logger"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func apiHandler(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
// 从Handler获取时区
timezone := h.GetTimezone()
now := tools.Now(timezone)
h.Success(map[string]interface{}{
"message": "Hello",
"timezone": timezone,
"time": tools.FormatDateTime(now),
})
}
func main() {
// 1. 配置logger异步模式输出到文件
loggerConfig := &logger.LoggerConfig{
Level: "info",
Output: "both", // 同时输出到stdout和文件
FilePath: "./logs/app.log",
Async: true,
BufferSize: 1000,
}
myLogger, err := logger.NewLogger(loggerConfig)
if err != nil {
log.Fatal(err)
}
defer myLogger.Close() // 确保程序退出时关闭logger
// 2. 配置CORS
corsConfig := &middleware.CORSConfig{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Content-Type", "Authorization", "X-Timezone"},
}
// 3. 配置日志中间件
loggingConfig := &middleware.LoggingConfig{
Logger: myLogger,
SkipPaths: []string{"/health", "/metrics"},
}
// 4. 配置Recovery中间件
recoveryConfig := &middleware.RecoveryConfig{
Logger: myLogger,
EnableStackTrace: true,
CustomHandler: func(w http.ResponseWriter, r *http.Request, err interface{}) {
h := commonhttp.NewHandler(w, r)
h.SystemError("服务器内部错误")
},
}
// 5. 配置限流中间件100请求/分钟)
rateLimiter := middleware.NewTokenBucketLimiter(100, time.Minute)
rateLimitConfig := &middleware.RateLimitConfig{
Limiter: rateLimiter,
OnRateLimitExceeded: func(w http.ResponseWriter, r *http.Request, key string) {
myLogger.Warnf(map[string]interface{}{
"key": key,
"path": r.URL.Path,
}, "Rate limit exceeded")
},
}
// 6. 创建中间件链(顺序很重要!)
chain := middleware.NewChain(
middleware.Recovery(recoveryConfig), // 最外层捕获panic
middleware.Logging(loggingConfig), // 日志记录
middleware.RateLimit(rateLimitConfig), // 限流
middleware.CORS(corsConfig), // CORS处理
middleware.Timezone, // 时区处理
)
// 7. 应用中间件
http.Handle("/api", chain.ThenFunc(apiHandler))
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
### 示例2基础的CORS + 时区中间件
```go
package main
import (
"log"
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
commonhttp "git.toowon.com/jimmy/go-common/http"
"git.toowon.com/jimmy/go-common/tools"
)
func apiHandler(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
// 从Handler获取时区
timezone := h.GetTimezone()
now := tools.Now(timezone)
h.Success(map[string]interface{}{
"message": "Hello",
"timezone": timezone,
"time": tools.FormatDateTime(now),
})
}
func main() {
// 创建简单的中间件链
chain := middleware.NewChain(
middleware.CORS(nil), // 使用默认CORS配置
middleware.Timezone, // 使用默认时区
)
http.Handle("/api", chain.ThenFunc(apiHandler))
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
### 示例2与路由框架集成
```go
package main
import (
"net/http"
"git.toowon.com/jimmy/go-common/middleware"
commonhttp "git.toowon.com/jimmy/go-common/http"
)
func main() {
mux := http.NewServeMux()
// 创建中间件链
chain := middleware.NewChain(
middleware.CORS(),
middleware.Timezone,
)
// 应用中间件到所有路由
mux.Handle("/api/users", chain.ThenFunc(getUsers))
mux.Handle("/api/posts", chain.ThenFunc(getPosts))
http.ListenAndServe(":8080", mux)
}
func getUsers(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
timezone := h.GetTimezone()
// 处理逻辑
h.Success(nil)
}
func getPosts(w http.ResponseWriter, r *http.Request) {
h := commonhttp.NewHandler(w, r)
timezone := h.GetTimezone()
// 处理逻辑
h.Success(nil)
}
```
## API 参考
### CORS中间件
#### CORS(config ...*CORSConfig) func(http.Handler) http.Handler
创建CORS中间件。
**参数:**
- `config`: 可选的CORS配置不指定则使用默认配置
**返回:** 中间件函数
#### DefaultCORSConfig() *CORSConfig
返回默认的CORS配置。
### 时区中间件
#### Timezone(next http.Handler) http.Handler
时区处理中间件(默认时区为 AsiaShanghai
#### TimezoneWithDefault(defaultTimezone string) func(http.Handler) http.Handler
时区处理中间件(可自定义默认时区)。
**参数:**
- `defaultTimezone`: 默认时区字符串
**返回:** 中间件函数
#### GetTimezoneFromContext(ctx context.Context) string
从context中获取时区。
### 日志中间件
#### Logging(config *LoggingConfig) func(http.Handler) http.Handler
创建日志中间件。
**参数:**
- `config`: 日志配置nil则使用默认配置
**返回:** 中间件函数
### Recovery中间件
#### Recovery(config *RecoveryConfig) func(http.Handler) http.Handler
创建Recovery中间件。
**参数:**
- `config`: Recovery配置nil则使用默认配置
**返回:** 中间件函数
#### RecoveryWithLogger(log *logger.Logger) func(http.Handler) http.Handler
使用指定logger的Recovery中间件便捷函数
#### RecoveryWithCustomHandler(customHandler func(...)) func(http.Handler) http.Handler
使用自定义错误处理的Recovery中间件便捷函数
### 限流中间件
#### RateLimit(config *RateLimitConfig) func(http.Handler) http.Handler
创建限流中间件。
**参数:**
- `config`: 限流配置nil则使用默认配置100请求/分钟)
**返回:** 中间件函数
#### NewTokenBucketLimiter(rate int, windowSize time.Duration) RateLimiter
创建令牌桶限流器。
**参数:**
- `rate`: 每个窗口期允许的请求数
- `windowSize`: 窗口大小
**返回:** 限流器实例
#### RateLimitWithRate(rate int, windowSize time.Duration) func(http.Handler) http.Handler
使用指定速率创建限流中间件(便捷函数)。
#### RateLimitByIP(rate int, windowSize time.Duration) func(http.Handler) http.Handler
按IP限流便捷函数
### 中间件链
#### NewChain(middlewares ...func(http.Handler) http.Handler) *Chain
创建新的中间件链。
#### (c *Chain) Then(handler http.Handler) http.Handler
将中间件链应用到处理器。
#### (c *Chain) ThenFunc(handler http.HandlerFunc) http.Handler
将中间件链应用到处理器函数。
#### (c *Chain) Append(middlewares ...func(http.Handler) http.Handler) *Chain
追加中间件到链中。
## 注意事项
### 1. CORS配置
- 生产环境建议明确指定允许的源,避免使用 "*"
- 如果使用凭证cookies必须明确指定源不能使用 "*"
- CORS中间件应该在Recovery和Logging之后以便正确处理预检请求
### 2. 时区处理
- 时区信息存储在context中确保中间件在处理器之前执行
- 时区验证失败时会自动回退到默认时区,不会返回错误
- 建议在CORS配置中包含 `X-Timezone` 请求头
### 3. 日志记录
- **生产环境推荐异步模式**:避免日志写入阻塞请求,提升性能
- **跳过高频接口**:健康检查、监控接口等高频接口建议跳过日志
- **日志轮转**使用文件输出时建议配合日志轮转工具如logrotate
- **敏感信息**:不要记录请求体和响应体,避免泄露敏感信息
### 4. Panic恢复
- **放在最外层**Recovery中间件应该放在中间件链的最前面
- **记录日志**务必配置logger确保panic信息被记录
- **监控告警**建议将panic事件接入监控系统及时发现问题
- **堆栈跟踪**:生产环境建议启用,方便排查问题
### 5. 限流配置
- **合理设置阈值**:根据实际业务需求设置限流阈值
- **分布式部署**当前实现使用内存存储适用于单机。分布式部署建议使用Redis
- **键设计**合理设计限流键可以按IP、用户、API等维度限流
- **响应头**客户端可以根据X-RateLimit-*响应头实现智能重试
### 6. 中间件顺序(推荐)
建议的中间件顺序(从外到内):
1. **Recovery** - 最外层捕获所有panic
2. **Logging** - 记录所有请求(包括限流的请求)
3. **RateLimit** - 限流保护
4. **CORS** - 处理跨域
5. **Timezone** - 时区处理
6. **业务中间件** - 认证、授权等
```go
chain := middleware.NewChain(
middleware.Recovery(recoveryConfig), // 1. Panic恢复
middleware.Logging(loggingConfig), // 2. 日志记录
middleware.RateLimit(rateLimitConfig), // 3. 限流
middleware.CORS(corsConfig), // 4. CORS
middleware.Timezone, // 5. 时区
)
```
### 7. 性能考虑
- **异步日志**使用异步logger避免IO阻塞
- **限流算法**:令牌桶算法支持突发流量
- **自动清理**:限流数据会自动清理,避免内存泄漏
- **跳过路径**合理使用SkipPaths减少不必要的处理
### 8. 生产环境建议
- 使用异步logger配置日志文件和轮转
- 启用Recovery中间件配置告警
- 根据业务设置合理的限流阈值
- 配置监控指标(请求量、错误率、限流触发次数等)
- 定期review日志优化性能瓶颈