Files
go-common/docs/middleware.md
2025-11-30 13:15:13 +08:00

10 KiB
Raw Blame History

中间件工具文档

概述

中间件工具提供了常用的HTTP中间件功能包括CORS处理和时区管理。

功能特性

  • CORS中间件:支持跨域资源共享配置
  • 时区中间件:从请求头读取时区信息,支持默认时区设置
  • 中间件链:提供便捷的中间件链式调用

CORS中间件

功能说明

CORS中间件用于处理跨域资源共享支持

  • 配置允许的源(支持通配符)
  • 配置允许的HTTP方法
  • 配置允许的请求头
  • 配置暴露的响应头
  • 支持凭证传递
  • 预检请求缓存时间设置

使用方法

基本使用(默认配置)

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/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)
}

自定义配置

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/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)
}

允许所有源(开发环境)

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. 如果 AllowCredentialstrueAllowedOrigins 不能使用 "*",必须指定具体的源
  2. 通配符支持:"*.example.com" 会匹配 "https://app.example.com" 等子域名
  3. 预检请求OPTIONS会自动处理无需在业务代码中处理

时区中间件

功能说明

时区中间件用于从请求头读取时区信息并存储到context中方便后续使用。

  • 从请求头 X-Timezone 读取时区
  • 如果未传递时区信息,使用默认时区 AsiaShanghai
  • 时区信息存储到context中可通过 http.GetTimezone() 获取
  • 自动验证时区有效性,无效时区会回退到默认时区

使用方法

基本使用(默认时区 AsiaShanghai

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/middleware"
    "git.toowon.com/jimmy/go-commom/http"
    "git.toowon.com/jimmy/go-commom/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)
}

自定义默认时区

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/middleware"
    "git.toowon.com/jimmy/go-commom/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)
}

在业务代码中使用时区

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/http"
    "git.toowon.com/jimmy/go-commom/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 请求头

中间件链

功能说明

中间件链提供便捷的中间件组合方式,支持链式调用。

使用方法

import (
    "net/http"
    "git.toowon.com/jimmy/go-commom/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)
}

链式追加中间件

chain := middleware.NewChain(middleware.CORS())
chain.Append(middleware.Timezone)

handler := chain.ThenFunc(handler)

完整示例

示例1CORS + 时区中间件

package main

import (
    "log"
    "net/http"
    
    "git.toowon.com/jimmy/go-commom/middleware"
    "git.toowon.com/jimmy/go-commom/http"
    "git.toowon.com/jimmy/go-commom/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与路由框架集成

package main

import (
    "net/http"
    
    "git.toowon.com/jimmy/go-commom/middleware"
    "git.toowon.com/jimmy/go-commom/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预检请求会被缓存减少重复请求
    • 时区验证只在请求头存在时进行,性能影响很小