3. HTTP 服务基础:net/http 包使用、路由注册、请求与响应处理、中间件概念

构建任务调度平台的第一步是搭建稳定、可扩展的 HTTP 服务。Go 标准库 net/http 提供了构建 Web 服务所需的核心原语,理解其工作原理是后续实现 OpenAPI 参数校验的基石。本章将系统讲解 HTTP 服务的启动、路由注册、请求/响应处理以及中间件模式。

3.1 net/http 包核心组件

net/http 包围绕两个核心接口构建:Handler 和 HandlerFunc。任何实现了 ServeHTTP(ResponseWriter, *Request) 方法的对象都可以作为 HTTP 处理器。

组件 类型 说明
http.Handler 接口 定义 ServeHTTP(ResponseWriter, *Request) 方法
http.HandlerFunc 函数类型 将普通函数适配为 Handler 的适配器
http.Server 结构体 配置服务器地址、端口、超时等参数
http.ServeMux 结构体 默认的路由多路复用器,支持路径匹配
核心要点: http.ListenAndServe 是启动服务的快捷方式,内部创建 http.Server 并调用 ListenAndServe 方法。生产环境建议使用 http.Server 结构体以配置超时等参数。

3.2 路由注册与请求分发

Go 1.22 之前的版本使用 http.ServeMux 进行路由匹配,支持精确路径和前缀匹配。Go 1.22 引入了增强路由,支持方法匹配和路径参数。

3.2.1 基础路由注册

package main

import (
    "fmt"
    "net/http"
)

func main() {
    // 方式一:使用默认 ServeMux
    http.HandleFunc("/api/tasks", handleTasks)
    http.HandleFunc("/api/tasks/", handleTaskByID) // 前缀匹配

    // 方式二:显式创建 ServeMux
    mux := http.NewServeMux()
    mux.HandleFunc("/api/health", handleHealth)
    mux.HandleFunc("/api/version", handleVersion)

    // 启动服务
    http.ListenAndServe(":8080", mux)
}

func handleTasks(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Task list endpoint")
}

func handleTaskByID(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Task detail endpoint")
}

func handleHealth(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, `{"status":"ok"}`)
}

3.2.2 Go 1.22+ 增强路由

// Go 1.22 支持方法匹配和路径参数
mux := http.NewServeMux()
mux.HandleFunc("GET /api/tasks", listTasks)
mux.HandleFunc("POST /api/tasks", createTask)
mux.HandleFunc("GET /api/tasks/{id}", getTaskByID)
mux.HandleFunc("PUT /api/tasks/{id}", updateTask)
mux.HandleFunc("DELETE /api/tasks/{id}", deleteTask)

func getTaskByID(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id") // 获取路径参数
    fmt.Fprintf(w, "Task ID: %s", id)
}
实践建议: 任务调度平台的 API 设计遵循 RESTful 风格,建议使用 Go 1.22+ 的增强路由,明确声明 HTTP 方法和路径参数,提升代码可读性和安全性。

3.3 请求与响应处理

每个 HTTP 请求都通过 *http.Request 对象暴露,响应通过 http.ResponseWriter 接口写入。正确处理请求解析和响应编码是参数校验的前提。

3.3.1 请求解析

数据来源 访问方式 示例
URL 查询参数 r.URL.Query() r.URL.Query().Get("page")
路径参数 r.PathValue() r.PathValue("id")
请求头 r.Header r.Header.Get("Content-Type")
JSON 请求体 json.NewDecoder(r.Body) json.NewDecoder(r.Body).Decode(&task)
表单数据 r.ParseForm() r.FormValue("name")

3.3.2 响应编码

func respondJSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

func respondError(w http.ResponseWriter, status int, message string) {
    respondJSON(w, status, map[string]string{"error": message})
}

// 使用示例
func createTask(w http.ResponseWriter, r *http.Request) {
    var task Task
    if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
        respondError(w, http.StatusBadRequest, "Invalid request body")
        return
    }
    // 业务逻辑...
    respondJSON(w, http.StatusCreated, task)
}
常见陷阱: 响应体必须在使用后关闭 r.Body.Close(),否则可能导致资源泄漏。标准库在请求处理结束后会自动关闭,但手动关闭是良好实践。另外,WriteHeader 必须在写入响应体之前调用,且只能调用一次。

3.4 中间件概念与实现

中间件是 HTTP 处理链中的可插拔组件,用于在请求到达处理器之前或之后执行横切关注点(如日志、认证、参数校验)。Go 中中间件的核心模式是 装饰器模式:一个中间件接收 http.Handler,返回一个新的 http.Handler。

3.4.1 中间件模式

// 中间件类型定义
type Middleware func(http.Handler) http.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))
    })
}

// 认证中间件
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" || !validateToken(token) {
            respondError(w, http.StatusUnauthorized, "Missing or invalid token")
            return
        }
        next.ServeHTTP(w, r)
    })
}

// 中间件链组合
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

3.4.2 中间件在任务调度平台中的应用

在任务调度平台中,中间件用于实现以下横切关注点:

  • 请求日志:记录每个 API 调用的方法、路径、耗时和状态码
  • 认证与鉴权:验证 API Key 或 JWT Token,提取用户身份
  • 参数校验:在进入业务逻辑前对请求参数进行格式和类型校验
  • 限流控制:基于 IP 或用户 ID 进行请求频率限制
  • 超时控制:为每个请求设置最大处理时间,防止资源耗尽
  • 恢复处理:捕获 panic,返回 500 错误,避免服务崩溃
中间件执行顺序: 中间件链的执行顺序与注册顺序相反。第一个注册的中间件最先处理请求,最后处理响应。例如 Chain(handler, LoggingMiddleware, AuthMiddleware) 的执行顺序是:Logging → Auth → Handler → Auth → Logging。

3.5 HTTP 服务架构总览

下图展示了任务调度平台 HTTP 服务的完整请求处理流程,从请求进入中间件链到最终响应返回:

HTTP 服务请求处理流程 客户端请求 中间件链(Middleware Chain) 日志中间件 认证中间件 参数校验 限流控制 路由分发(ServeMux) 业务处理器(Handler)

3.6 完整服务启动示例

以下代码展示了如何将上述概念整合为一个可运行的任务调度 HTTP 服务:

package main

import (
    "log"
    "net/http"
    "time"
)

func main() {
    mux := http.NewServeMux()

    // 注册任务相关路由
    mux.HandleFunc("GET /api/v1/tasks", listTasks)
    mux.HandleFunc("POST /api/v1/tasks", createTask)
    mux.HandleFunc("GET /api/v1/tasks/{id}", getTask)
    mux.HandleFunc("PUT /api/v1/tasks/{id}", updateTask)
    mux.HandleFunc("DELETE /api/v1/tasks/{id}", deleteTask)

    // 注册健康检查
    mux.HandleFunc("GET /api/v1/health", healthCheck)

    // 构建中间件链
    handler := Chain(mux,
        RecoveryMiddleware,   // 恢复处理
        LoggingMiddleware,    // 请求日志
        AuthMiddleware,       // 认证鉴权
        RateLimitMiddleware,  // 限流控制
    )

    // 配置服务器
    server := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    log.Printf("Task scheduler API server starting on :8080")
    if err := server.ListenAndServe(); err != nil {
        log.Fatalf("Server failed: %v", err)
    }
}
生产环境配置: 始终为 http.Server 设置超时参数。ReadTimeout 防止慢客户端攻击,WriteTimeout 防止响应写入阻塞,IdleTimeout 控制 Keep-Alive 连接的空闲时间。这些配置是保障服务稳定性的基础。

3.7 本章小结

本章涵盖了使用 Go 标准库 net/http 构建 HTTP 服务的完整知识体系:

  • 核心组件:Handler、HandlerFunc、ServeMux、Server 的关系与使用方式
  • 路由注册:基础路径匹配与 Go 1.22+ 增强路由的方法匹配和路径参数
  • 请求与响应:解析查询参数、路径参数、请求头、JSON 体,以及标准化的 JSON 响应编码
  • 中间件模式:装饰器模式实现横切关注点,中间件链的组合与执行顺序
  • 服务配置:超时设置、优雅启动与关闭