3. HTTP 服务基础:标准库 net/http 构建 RESTful API、路由设计、请求与响应处理
在 Go 语言生态中,标准库 net/http 是构建 HTTP 服务的基石。它提供了完整的 HTTP 客户端与服务端实现,无需依赖任何第三方框架即可构建高性能的 RESTful API。本章将深入剖析如何使用标准库进行路由设计、请求解析与响应构造,为后续的中间件链路工程打下坚实基础。
3.1 核心组件与请求生命周期
一个标准的 Go HTTP 服务由三个核心组件构成:处理器(Handler)、多路复用器(ServeMux) 和 服务器(Server)。下图展示了请求从接收到响应的完整生命周期。
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()。正确的调用顺序至关重要。
以下代码展示了正确的响应构造顺序,并演示了常见的 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实现横切关注点,为后续链路工程奠定基础。