439 lines
10 KiB
Markdown
439 lines
10 KiB
Markdown
# 中间件工具文档
|
||
|
||
## 概述
|
||
|
||
中间件工具提供了常用的HTTP中间件功能,包括CORS处理和时区管理。
|
||
|
||
## 功能特性
|
||
|
||
- **CORS中间件**:支持跨域资源共享配置
|
||
- **时区中间件**:从请求头读取时区信息,支持默认时区设置
|
||
- **中间件链**:提供便捷的中间件链式调用
|
||
|
||
## 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中,可通过 `http.GetTimezone()` 获取
|
||
- 自动验证时区有效性,无效时区会回退到默认时区
|
||
|
||
### 使用方法
|
||
|
||
#### 基本使用(默认时区 AsiaShanghai)
|
||
|
||
```go
|
||
import (
|
||
"net/http"
|
||
"git.toowon.com/jimmy/go-common/middleware"
|
||
"git.toowon.com/jimmy/go-common/http"
|
||
"git.toowon.com/jimmy/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"
|
||
"git.toowon.com/jimmy/go-common/middleware"
|
||
"git.toowon.com/jimmy/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"
|
||
"git.toowon.com/jimmy/go-common/http"
|
||
"git.toowon.com/jimmy/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"
|
||
"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:CORS + 时区中间件
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"log"
|
||
"net/http"
|
||
|
||
"git.toowon.com/jimmy/go-common/middleware"
|
||
"git.toowon.com/jimmy/go-common/http"
|
||
"git.toowon.com/jimmy/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"
|
||
|
||
"git.toowon.com/jimmy/go-common/middleware"
|
||
"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) {
|
||
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预检请求会被缓存,减少重复请求
|
||
- 时区验证只在请求头存在时进行,性能影响很小
|
||
|