Files
go-common/docs/middleware.md

439 lines
10 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.

# 中间件工具文档
## 概述
中间件工具提供了常用的HTTP中间件功能包括CORS处理和时区管理。
## 功能特性
- **CORS中间件**:支持跨域资源共享配置
- **时区中间件**:从请求头读取时区信息,支持默认时区设置
- **中间件链**:提供便捷的中间件链式调用
## CORS中间件
### 功能说明
CORS中间件用于处理跨域资源共享支持
- 配置允许的源(支持通配符)
- 配置允许的HTTP方法
- 配置允许的请求头
- 配置暴露的响应头
- 支持凭证传递
- 预检请求缓存时间设置
### 使用方法
#### 基本使用(默认配置)
```go
import (
"net/http"
"github.com/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"
"github.com/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中可通过 `http.GetTimezone()` 获取
- 自动验证时区有效性,无效时区会回退到默认时区
### 使用方法
#### 基本使用(默认时区 AsiaShanghai
```go
import (
"net/http"
"github.com/go-common/middleware"
"github.com/go-common/http"
"github.com/go-common/datetime"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 从context获取时区
timezone := http.GetTimezone(r)
// 使用时区
now := datetime.Now(timezone)
datetime.FormatDateTime(now, timezone)
http.Success(w, map[string]interface{}{
"timezone": timezone,
"time": datetime.FormatDateTime(now),
})
}
func main() {
handler := middleware.Timezone(http.HandlerFunc(handler))
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 自定义默认时区
```go
import (
"net/http"
"github.com/go-common/middleware"
"github.com/go-common/datetime"
)
func handler(w http.ResponseWriter, r *http.Request) {
// 处理请求
}
func main() {
// 使用自定义默认时区
handler := middleware.TimezoneWithDefault(datetime.UTC)(http.HandlerFunc(handler))
http.Handle("/api", handler)
http.ListenAndServe(":8080", nil)
}
```
#### 在业务代码中使用时区
```go
import (
"net/http"
"github.com/go-common/http"
"github.com/go-common/datetime"
)
func GetUserList(w http.ResponseWriter, r *http.Request) {
// 从请求context获取时区
timezone := http.GetTimezone(r)
// 使用时区进行时间处理
now := datetime.Now(timezone)
// 查询数据时使用时区
startTime := datetime.StartOfDay(now, timezone)
endTime := datetime.EndOfDay(now, timezone)
// 返回数据
http.Success(w, map[string]interface{}{
"timezone": timezone,
"startTime": datetime.FormatDateTime(startTime),
"endTime": datetime.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` 请求头
## 中间件链
### 功能说明
中间件链提供便捷的中间件组合方式,支持链式调用。
### 使用方法
```go
import (
"net/http"
"github.com/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)
```
## 完整示例
### 示例1CORS + 时区中间件
```go
package main
import (
"log"
"net/http"
"github.com/go-common/middleware"
"github.com/go-common/http"
"github.com/go-common/datetime"
)
func apiHandler(w http.ResponseWriter, r *http.Request) {
// 获取时区
timezone := http.GetTimezone(r)
now := datetime.Now(timezone)
http.Success(w, map[string]interface{}{
"message": "Hello",
"timezone": timezone,
"time": datetime.FormatDateTime(now),
})
}
func main() {
// 配置CORS
corsConfig := &middleware.CORSConfig{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
AllowedHeaders: []string{"Content-Type", "Authorization", "X-Timezone"},
}
// 创建中间件链
chain := middleware.NewChain(
middleware.CORS(corsConfig),
middleware.Timezone,
)
// 应用中间件
handler := chain.ThenFunc(apiHandler)
http.Handle("/api", handler)
log.Println("Server started on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
### 示例2与路由框架集成
```go
package main
import (
"net/http"
"github.com/go-common/middleware"
"github.com/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) {
timezone := http.GetTimezone(r)
// 处理逻辑
http.Success(w, nil)
}
func getPosts(w http.ResponseWriter, r *http.Request) {
timezone := http.GetTimezone(r)
// 处理逻辑
http.Success(w, 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中获取时区。
### 中间件链
#### 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必须明确指定源不能使用 "*"
2. **时区处理**
- 时区信息存储在context中确保中间件在处理器之前执行
- 时区验证失败时会自动回退到默认时区,不会返回错误
3. **中间件顺序**
- CORS中间件应该放在最外层以便处理预检请求
- 时区中间件可以放在CORS之后
4. **性能考虑**
- CORS预检请求会被缓存减少重复请求
- 时区验证只在请求头存在时进行,性能影响很小