# 中间件工具文档 ## 概述 中间件工具提供了常用的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中,可通过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/datetime" ) func handler(w http.ResponseWriter, r *http.Request) { h := commonhttp.NewHandler(w, r) // 从Handler获取时区 timezone := h.GetTimezone() // 使用时区 now := datetime.Now(timezone) datetime.FormatDateTime(now, timezone) h.Success(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" commonhttp "git.toowon.com/jimmy/go-common/http" "git.toowon.com/jimmy/go-common/datetime" ) func GetUserList(w http.ResponseWriter, r *http.Request) { h := commonhttp.NewHandler(w, r) // 从Handler获取时区 timezone := h.GetTimezone() // 使用时区进行时间处理 now := datetime.Now(timezone) // 查询数据时使用时区 startTime := datetime.StartOfDay(now, timezone) endTime := datetime.EndOfDay(now, timezone) // 返回数据 h.Success(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" commonhttp "git.toowon.com/jimmy/go-common/http" "git.toowon.com/jimmy/go-common/datetime" ) func apiHandler(w http.ResponseWriter, r *http.Request) { h := commonhttp.NewHandler(w, r) // 从Handler获取时区 timezone := h.GetTimezone() now := datetime.Now(timezone) h.Success(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" 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中获取时区。 ### 中间件链 #### 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预检请求会被缓存,减少重复请求 - 时区验证只在请求头存在时进行,性能影响很小