2. Go Web 基础回顾:标准库 net/http 快速搭建一个简单的 RESTful API 服务
在深入接口文档生成与自动化测试之前,有必要重新审视 Go 标准库 net/http 的核心能力。Go 语言无需第三方框架即可构建生产级别的 RESTful API,其内置的并发模型与简洁的接口设计,为后续集成 Swagger 文档与测试工具奠定了坚实基础。
2.1 net/http 核心组件与请求生命周期
标准库将 HTTP 请求处理抽象为三个核心角色:路由器(ServeMux)、处理器(Handler) 与 服务器(Server)。下图展示了从客户端请求到响应返回的完整链路:
2.2 快速搭建 RESTful API:用户资源示例
以下代码使用标准库实现一个针对“用户(User)”资源的 CRUD API,包含 GET、POST、PUT、DELETE 四个端点。这是后续章节生成 OpenAPI 文档与编写自动化测试的基础原型。
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"strings"
"sync"
)
// User 定义用户数据结构
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
// userStore 模拟内存数据库
type userStore struct {
mu sync.RWMutex
users map[int]User
nextID int
}
var store = &userStore{
users: make(map[int]User),
nextID: 1,
}
// 用户处理器:根据路径分发
func userHandler(w http.ResponseWriter, r *http.Request) {
// 提取路径中的 ID 部分
path := strings.TrimPrefix(r.URL.Path, "/api/users")
path = strings.TrimPrefix(path, "/")
switch r.Method {
case http.MethodGet:
if path == "" {
listUsers(w, r)
} else {
id, _ := strconv.Atoi(path)
getUser(w, r, id)
}
case http.MethodPost:
createUser(w, r)
case http.MethodPut:
id, _ := strconv.Atoi(path)
updateUser(w, r, id)
case http.MethodDelete:
id, _ := strconv.Atoi(path)
deleteUser(w, r, id)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
// 具体业务函数(以 listUsers 为例)
func listUsers(w http.ResponseWriter, r *http.Request) {
store.mu.RLock()
defer store.mu.RUnlock()
users := make([]User, 0, len(store.users))
for _, u := range store.users {
users = append(users, u)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// createUser, getUser, updateUser, deleteUser 实现略(结构类似)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/users", userHandler)
mux.HandleFunc("/api/users/", userHandler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
log.Println("Server starting on :8080")
log.Fatal(server.ListenAndServe())
}
2.3 路由分发模式对比
标准库 ServeMux 的路由规则较为朴素,与第三方框架存在明显差异。下表总结了常见路由模式及其适用场景:
| 路由模式 | 示例路径 | 匹配规则 | 适用场景 |
|---|---|---|---|
| 精确匹配 | /api/users |
仅匹配该路径 | 集合操作(GET/POST 列表) |
| 前缀匹配 | /api/users/ |
匹配以该前缀开头的所有路径 | 单资源操作(含 ID 路径) |
| 路径参数提取 | 手动 strings.TrimPrefix |
开发者自行解析 ID 或参数 | 标准库原生方案 |
| 第三方路由(如 gorilla/mux) | /api/users/{id} |
内置路径变量与正则约束 | 复杂路由需求 |
实践建议: 对于中小型项目,标准库
ServeMux 配合手动路径解析完全够用。它减少了依赖,且让路由逻辑显式可见,非常适合教学与接口文档生成场景。
2.4 中间件模式:为 API 添加通用功能
标准库通过 装饰器模式 实现中间件。以下示例展示了如何为所有 API 端点添加请求日志与 CORS 头:
// 日志中间件
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)
})
}
// CORS 中间件
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// 在 main 中链式使用
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/users", userHandler)
mux.HandleFunc("/api/users/", userHandler)
// 中间件链:先 CORS,再日志,最后路由
handler := corsMiddleware(loggingMiddleware(mux))
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
log.Fatal(server.ListenAndServe())
}
2.5 常见陷阱与最佳实践
- 路径尾部斜杠陷阱:
/api/users与/api/users/是两个不同的注册路径。前者只匹配精确路径,后者匹配所有子路径。务必同时注册两者以覆盖集合与单资源操作。 - ResponseWriter 写入顺序: 先设置 Header,再写入状态码(
WriteHeader),最后写入 Body。Header 必须在WriteHeader之前设置,否则会被忽略。 - JSON 编码错误处理:
json.NewEncoder(w).Encode()在编码失败时不会自动返回错误状态码。建议先编码到bytes.Buffer,再写入w,以便在编码失败时返回 500 状态码。 - 并发安全: 所有 Handler 默认在独立 goroutine 中执行,共享数据(如
userStore)必须使用sync.RWMutex保护。
注意: 标准库
ServeMux 不支持路径参数(如 /api/users/:id)或方法路由。如果项目路由规则复杂,建议在后续章节引入 gorilla/mux 或 chi 等轻量路由库。但本课程将始终基于标准库构建,以保持零依赖的教学优势。
2.6 本章小结
通过标准库 net/http,我们仅用不到 100 行代码就构建了一个完整的 RESTful API 服务。这个原型具备以下特征:
- 纯标准库实现,无外部依赖
- 清晰的路径分发与中间件链
- 并发安全的模拟数据存储
- 标准的 JSON 响应格式