3. HTTP 网关基础:标准库 net/http、中间件模式与请求上下文传递

构建权限审计网关的第一步,是掌握 Go 语言原生的 HTTP 处理能力。标准库 net/http 提供了完整的 HTTP 服务器与客户端实现,而中间件模式与 context.Context 则是网关扩展性、可观测性与安全性的基石。本章将围绕这三个核心概念展开,为后续实现错误码断言与审计拦截打下坚实基础。

3.1 标准库 net/http 核心组件

net/http 的设计围绕 Handler 接口展开,该接口定义了 HTTP 处理的基本契约:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

网关开发中常用的核心组件包括:

组件 作用 典型使用场景
http.Handler HTTP 处理接口 所有路由、中间件、最终处理器的统一抽象
http.HandlerFunc 函数适配器 将普通函数快速转换为 Handler
http.ServeMux 默认路由复用器 基于路径的请求分发(Go 1.22 起支持方法和模式匹配)
http.ResponseWriter 响应写入接口 设置状态码、Header、写入响应体
http.Request 请求对象 读取请求方法、URL、Header、Body、上下文
网关实践要点: 在权限审计网关中,http.ResponseWriter 通常会被自定义包装,用于拦截写入的状态码和响应体,以便在审计日志中记录原始错误码与断言结果。

3.2 中间件模式:链式处理与职责分离

中间件模式是网关架构的核心设计模式。它将横切关注点(如日志、认证、审计、限流)从业务处理中剥离,形成可组合的处理链。一个中间件本质上是一个接收 Handler 并返回新 Handler 的函数:

type Middleware func(http.Handler) http.Handler

典型的中间件链执行流程如下:

  1. 请求进入:外部请求到达网关。
  2. 前置处理:中间件按注册顺序依次执行前置逻辑(如解析 Token、提取用户信息)。
  3. 核心处理:最终的业务 Handler 执行,生成响应。
  4. 后置处理:中间件按逆序执行后置逻辑(如记录响应状态码、计算耗时)。
设计建议: 在权限审计网关中,建议将审计中间件放在认证中间件之后、业务 Handler 之前,确保审计日志能记录到已认证的用户身份和最终的断言结果。

以下是一个简单的日志中间件实现示例:

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        // 前置处理:记录请求信息
        log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
        // 调用下一个处理器
        next.ServeHTTP(w, r)
        // 后置处理:记录耗时
        log.Printf("[%s] %s completed in %v", r.Method, r.URL.Path, time.Since(start))
    })
}

3.3 请求上下文 (context.Context) 传递

在网关中,请求上下文是贯穿整个请求生命周期的数据载体。Go 的 context.Context 提供了超时控制、取消信号和键值对存储能力。在中间件链中,通过 context.WithValue 将解析后的用户身份、请求 ID、审计元数据注入上下文,后续中间件和业务 Handler 通过 r.Context() 获取。

安全注意事项: context.WithValue 的键应使用自定义类型,避免与标准库或其他第三方库的键冲突。推荐使用包级别的私有类型作为键。
// 定义上下文键类型,避免冲突
type contextKey string

const (
    UserIDKey    contextKey = "user_id"
    RequestIDKey contextKey = "request_id"
)

// 认证中间件:将用户 ID 注入上下文
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 模拟认证过程
        userID := extractUserIDFromToken(r)
        ctx := context.WithValue(r.Context(), UserIDKey, userID)
        // 传递新的请求对象
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// 业务 Handler:从上下文读取用户 ID
func BusinessHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.Context().Value(UserIDKey).(string)
    // 处理业务逻辑...
}

3.4 网关请求处理流程架构

下图展示了权限审计网关中,中间件链与上下文传递的完整流程:

权限审计网关请求处理流程 HTTP 请求入口 中间件 1:日志 / 请求 ID 中间件 2:认证 / 用户注入 中间件 3:权限审计 / 错误码断言 业务 Handler HTTP 响应出口 context.Context 传递 请求 ID 用户身份 (UserID) 审计元数据 断言上下文 通过 r.WithContext(ctx) 传递 后置处理(逆序) 中间件 3:记录断言结果 中间件 2:记录用户操作 中间件 1:记录响应耗时 通过包装 ResponseWriter 实现

3.5 自定义 ResponseWriter 包装

在权限审计网关中,中间件需要获取业务 Handler 写入的 HTTP 状态码和响应体,以便进行错误码断言。这通常通过包装 http.ResponseWriter 实现:

type ResponseWriterWrapper struct {
    http.ResponseWriter
    statusCode int
    body       bytes.Buffer
}

func (w *ResponseWriterWrapper) WriteHeader(code int) {
    w.statusCode = code
    w.ResponseWriter.WriteHeader(code)
}

func (w *ResponseWriterWrapper) Write(data []byte) (int, error) {
    w.body.Write(data)
    return w.ResponseWriter.Write(data)
}

// 审计中间件中使用
func AuditMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        wrapper := &ResponseWriterWrapper{ResponseWriter: w, statusCode: http.StatusOK}
        next.ServeHTTP(wrapper, r)
        // 此时 wrapper.statusCode 和 wrapper.body 包含最终响应信息
        // 可进行错误码断言与审计记录
    })
}

3.6 本章小结

  • net/http 标准库 提供了 Handler 接口和 ServeMux 路由,是网关开发的基础设施。
  • 中间件模式 通过函数式组合实现横切关注点的分离,支持前置/后置处理,是网关扩展性的关键。
  • context.Context 在中间件链中传递请求级数据,确保用户身份、请求 ID、审计元数据等信息的线程安全传递。
  • ResponseWriter 包装 是拦截响应状态码和响应体的标准手法,为错误码断言提供数据来源。