3. HTTP 基础库与路由:标准库 net/http 详解、http.ServeMux 路由、自定义 Handler 与中间件模式
本章深入 Go 标准库 net/http 的核心机制,从底层处理模型到上层路由构建,再到可复用的中间件模式,为后续实现 API 网关奠定坚实基础。你将掌握如何不依赖第三方框架,仅用标准库搭建出结构清晰、可扩展的 HTTP 服务。
3.1 net/http 核心处理模型:Handler 与 HandlerFunc
net/http 的核心是一个简洁的接口:Handler。任何实现了该接口的类型都可以处理 HTTP 请求。
| 组件 | 类型 | 说明 |
|---|---|---|
| Handler | interface |
定义 ServeHTTP(ResponseWriter, *Request) 方法 |
| HandlerFunc | func 类型 |
将普通函数适配为 Handler 的适配器 |
| ServeMux | struct |
默认的路由多路复用器,实现 Handler 接口 |
| Server | struct |
监听端口、管理连接、调用 Handler 的运行时容器 |
关键理解:整个
net/http 服务器就是一个 Handler 链。从 Server 到 ServeMux,再到你的业务逻辑,每一层都实现了 ServeHTTP 方法。
// 最基础的 Handler 实现
type helloHandler struct{}
func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, Go Gateway!"))
}
// 使用 HandlerFunc 适配器
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
}
func main() {
mux := http.NewServeMux()
mux.Handle("/hello", &helloHandler{})
mux.HandleFunc("/health", healthCheckHandler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
server.ListenAndServe()
}
3.2 http.ServeMux 路由机制与匹配规则
http.ServeMux 是 Go 标准库内置的路由器,采用前缀匹配策略。理解其匹配规则是避免路由冲突的关键。
路由注册与匹配规则
- 精确路径:注册
/api只匹配/api,不匹配/api/v1。 - 子树路径:注册
/api/(以斜杠结尾)匹配/api/及其所有子路径/api/v1、/api/v1/users。 - 最长匹配优先:同时注册
/和/api/,请求/api/v1会匹配/api/而非/。 - 根路径兜底:注册
/会捕获所有未被其他模式匹配的请求,通常用作 404 处理器。
实战建议:在 API 网关中,建议将根路径
/ 保留给健康检查和指标端点,将业务路由注册为明确的子树路径,如 /api/v1/、/admin/。
func main() {
mux := http.NewServeMux()
// 精确匹配
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("health check"))
})
// 子树匹配 - 所有 /api/ 开头的请求
mux.Handle("/api/", http.StripPrefix("/api/", apiHandler()))
// 根路径兜底
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 - Not Found"))
})
http.ListenAndServe(":8080", mux)
}
func apiHandler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/users", listUsers)
mux.HandleFunc("/orders", listOrders)
return mux
}
3.3 自定义 Handler:构建可组合的 HTTP 处理单元
通过自定义 Handler 结构体,我们可以将请求处理的不同关注点(认证、日志、限流、业务逻辑)封装为独立的处理单元,并通过组合方式构建处理链。
// 自定义 Handler 结构体 - 携带依赖
type UserHandler struct {
db *sql.DB
logger *log.Logger
cache *redis.Client
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 根据请求方法分发
switch r.Method {
case http.MethodGet:
h.getUser(w, r)
case http.MethodPost:
h.createUser(w, r)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}
func (h *UserHandler) getUser(w http.ResponseWriter, r *http.Request) {
// 从路径中提取 ID
id := r.URL.Query().Get("id")
// 业务逻辑...
w.Write([]byte("user: " + id))
}
3.4 中间件模式:函数式装饰器与链式组合
中间件模式是 Go HTTP 开发的核心模式。一个中间件是一个接收 Handler 并返回 Handler 的函数,通过嵌套调用形成处理链。
标准中间件签名
// 标准中间件签名
type Middleware func(http.Handler) http.Handler
// 链式组合工具
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
实战中间件示例
// 请求日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 包装 ResponseWriter 以捕获状态码
lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(lrw, r)
log.Printf("[%s] %s %s - %d (%v)",
r.Method, r.URL.Path, r.RemoteAddr, lrw.statusCode, time.Since(start))
})
}
// 恢复中间件 - 防止 panic 导致进程崩溃
func RecoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("PANIC: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
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, Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
// 使用示例
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/users", userHandler)
// 链式组合中间件
handler := Chain(mux,
RecoveryMiddleware,
LoggingMiddleware,
CORSMiddleware,
)
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
server.ListenAndServe()
}
注意:中间件的执行顺序与组合顺序相反。在
Chain 函数中,最后一个传入的中间件最先执行。上述示例中,请求到达顺序为:CORS → Logging → Recovery → ServeMux。
3.5 路由与中间件集成架构
将路由、中间件和业务 Handler 整合为一个清晰的架构,是构建可维护 API 网关的基础。
完整集成示例
// 路由分组与中间件集成
func setupRouter() http.Handler {
mux := http.NewServeMux()
// 健康检查 - 无需中间件
mux.HandleFunc("/health", healthHandler)
// API v1 路由组 - 需要认证和验证
apiV1 := http.NewServeMux()
apiV1.HandleFunc("/users", userHandler)
apiV1.HandleFunc("/orders", orderHandler)
// 为 API 路由组添加子中间件
apiHandler := Chain(apiV1,
ValidationMiddleware,
AuthMiddleware,
)
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", apiHandler))
// 管理后台 - 需要管理员认证和审计
admin := http.NewServeMux()
admin.HandleFunc("/dashboard", dashboardHandler)
admin.HandleFunc("/config", configHandler)
adminHandler := Chain(admin,
AuditMiddleware,
AdminAuthMiddleware,
)
mux.Handle("/admin/", http.StripPrefix("/admin", adminHandler))
// 全局中间件
return Chain(mux,
RecoveryMiddleware,
LoggingMiddleware,
CORSMiddleware,
RateLimitMiddleware,
)
}
func main() {
handler := setupRouter()
server := &http.Server{
Addr: ":8080",
Handler: handler,
}
log.Fatal(server.ListenAndServe())
}
架构要点:全局中间件处理横切关注点(日志、恢复、CORS),路由组中间件处理业务相关关注点(认证、验证),业务 Handler 只关注核心逻辑。这种分层架构使得每个组件职责单一,易于测试和替换。