3. HTTP 服务基础:标准库 net/http 构建 RESTful API、路由设计、请求与响应处理

在 Go 语言生态中,标准库 net/http 是构建 HTTP 服务的基石。它提供了完整的 HTTP 客户端与服务端实现,无需依赖任何第三方框架即可构建高性能的 RESTful API。本章将深入剖析如何使用标准库进行路由设计、请求解析与响应构造,为后续的中间件链路工程打下坚实基础。

3.1 核心组件与请求生命周期

一个标准的 Go HTTP 服务由三个核心组件构成:处理器(Handler)、多路复用器(ServeMux) 和 服务器(Server)。下图展示了请求从接收到响应的完整生命周期。

Go net/http 请求生命周期 Go net/http 请求生命周期 客户端请求 http.Server ServeMux 路由 业务 Handler 响应返回客户端 关键说明: 1. Server 监听端口,接收 TCP 连接后解析 HTTP 请求。 2. ServeMux 根据请求路径匹配注册的路由模式,分发到对应 Handler。 3. Handler 处理业务逻辑,构造响应并写回客户端。

3.2 路由设计:ServeMux 与模式匹配

http.ServeMux 是 Go 标准库提供的默认多路复用器。它支持两种路由模式:精确匹配 和 子树匹配。理解这两种模式是正确设计 API 路由的前提。

路由模式 示例 匹配规则 典型场景
精确匹配 /api/users 仅匹配路径完全等于 /api/users 的请求 单个资源端点
子树匹配 /api/users/ 匹配以 /api/users/ 开头的所有路径(含 /api/users) 资源集合或嵌套路由
根路径 / 匹配所有未被其他模式匹配的请求(兜底) 404 处理或静态文件
最佳实践:RESTful API 设计中,建议对集合资源使用子树匹配(如 /api/users/),对单个资源使用精确匹配(如 /api/users/123 需自行解析路径参数)。标准库不直接支持路径参数,需通过 strings.TrimPrefix 或第三方库实现。

3.3 构建 RESTful API:处理器实现

Go 的 http.Handler 接口仅需实现一个方法:ServeHTTP(ResponseWriter, *Request)。以下是一个完整的用户资源 CRUD 示例,展示如何基于标准库构建 RESTful 风格 API。

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "strings"
)

// User 用户模型
type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

// UserHandler 用户资源处理器
type UserHandler struct {
    users []User
}

// ServeHTTP 实现 http.Handler 接口
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 提取路径中的 ID 部分
    path := strings.TrimPrefix(r.URL.Path, "/api/users/")
    id := strings.TrimSuffix(path, "/")

    switch r.Method {
    case http.MethodGet:
        if id == "" {
            h.listUsers(w, r) // GET /api/users/
        } else {
            h.getUser(w, r, id) // GET /api/users/{id}
        }
    case http.MethodPost:
        h.createUser(w, r) // POST /api/users/
    case http.MethodPut:
        h.updateUser(w, r, id) // PUT /api/users/{id}
    case http.MethodDelete:
        h.deleteUser(w, r, id) // DELETE /api/users/{id}
    default:
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
}

func (h *UserHandler) listUsers(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(h.users)
}

func (h *UserHandler) createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Invalid request body", http.StatusBadRequest)
        return
    }
    user.ID = len(h.users) + 1
    h.users = append(h.users, user)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

// getUser、updateUser、deleteUser 实现略

func main() {
    handler := &UserHandler{
        users: []User{},
    }
    // 注册子树匹配路由
    http.Handle("/api/users/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

3.4 请求处理:解析参数与请求体

标准库提供了丰富的请求解析能力,涵盖查询参数、表单数据、路径参数(需手动解析)和 JSON 请求体。下表总结了常用解析方法及其适用场景。

数据来源 解析方法 示例代码 注意事项
查询参数 r.URL.Query() r.URL.Query().Get("page") 返回 url.Values 类型,支持多值
表单数据 r.ParseForm() + r.Form r.FormValue("username") 需先调用 ParseForm,支持 URL 编码和 multipart
路径参数 手动解析(标准库) strings.TrimPrefix(r.URL.Path, "/api/users/") 标准库不原生支持,需自行实现或使用第三方路由
JSON 请求体 json.NewDecoder(r.Body) json.NewDecoder(r.Body).Decode(&user) 注意关闭 r.Body(标准库自动处理)
常见陷阱:调用 r.FormValue() 或 r.PostFormValue() 前,务必确保已调用 r.ParseForm() 或 r.ParseMultipartForm(),否则返回空值。标准库不会自动解析表单数据。

3.5 响应处理:状态码、Header 与 Body

构造 HTTP 响应时,http.ResponseWriter 接口提供了三个核心方法:WriteHeader()、Header() 和 Write()。正确的调用顺序至关重要。

HTTP 响应构造流程 HTTP 响应构造流程 1. 设置 Header 2. 写入状态码 3. 写入 Body 核心规则: • Header 必须在 WriteHeader 之前设置,否则 Header 将被忽略。 • WriteHeader 必须在 Write 之前调用,否则默认返回 200 OK。 • 调用 Write 后,ResponseWriter 将自动发送 Header 和状态码(若未显式调用 WriteHeader)。

以下代码展示了正确的响应构造顺序,并演示了常见的 JSON 响应模式。

// 成功响应
func respondJSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

// 错误响应
func respondError(w http.ResponseWriter, status int, message string) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(map[string]string{"error": message})
}

// 使用示例
func (h *UserHandler) getUser(w http.ResponseWriter, r *http.Request, id string) {
    // 模拟查找用户
    user := User{ID: 1, Name: "Alice", Age: 30}
    respondJSON(w, http.StatusOK, user)
}

func (h *UserHandler) deleteUser(w http.ResponseWriter, r *http.Request, id string) {
    // 模拟删除逻辑
    respondJSON(w, http.StatusNoContent, nil) // 204 No Content
}

3.6 中间件模式:为路由添加通用逻辑

标准库的 http.Handler 接口天然支持中间件模式。通过包装 Handler,可以在不修改业务逻辑的前提下添加日志、鉴权、超时等通用功能。这是后续章节构建中间件链路的基础。

// 日志中间件
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
        next.ServeHTTP(w, r)
    })
}

// 鉴权中间件
func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        // 验证 token 逻辑...
        next.ServeHTTP(w, r)
    })
}

func main() {
    handler := &UserHandler{}
    // 链式包装中间件
    wrappedHandler := loggingMiddleware(authMiddleware(handler))
    http.Handle("/api/users/", wrappedHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}
工程要点:标准库的中间件模式遵循“洋葱模型”——请求从外层中间件向内传递,响应从内层向外传递。中间件的执行顺序与包装顺序相反:loggingMiddleware(authMiddleware(handler)) 中,请求先经过 logging,再经过 auth,最后到达 handler。

3.7 本章小结

  • 路由设计:ServeMux 支持精确匹配和子树匹配,RESTful API 推荐使用子树匹配处理集合资源。
  • 请求解析:标准库提供 Query()、FormValue()、json.Decoder 等工具,路径参数需手动解析。
  • 响应构造:严格遵循 Header → WriteHeader → Write 的顺序,推荐封装 respondJSON 和 respondError 工具函数。
  • 中间件模式:通过包装 http.Handler 实现横切关注点,为后续链路工程奠定基础。