第42课:Handler 与中间件
学习目标
- 理解
http.Handler接口的核心定义与作用 - 掌握
http.HandlerFunc适配器的使用方法 - 学会创建自定义的 HTTP Handler 处理函数
- 理解中间件(Middleware)的设计模式与工作原理
- 能够实现一个简单的日志记录中间件
核心概念
1. Handler 接口
在 Go 的 net/http 包中,Handler 是一个接口,它只定义了一个方法:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
任何实现了 ServeHTTP 方法的类型都可以用来处理 HTTP 请求。你可以把它想象成一个收银台:客户(请求)来了,收银员(Handler)负责处理结账(生成响应)。
2. HandlerFunc 适配器
为了方便使用普通函数作为 Handler,Go 提供了一个适配器类型:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
这就像给普通员工(函数)发放了收银员资格证书(实现 Handler 接口),使其可以直接上岗。
3. 中间件模式
中间件是一种拦截器模式,它像洋葱一样层层包裹着真正的业务处理器。请求从外到内穿过各层中间件,响应则从内到外穿出。每个中间件可以:
- 在调用下一层之前执行代码(请求阶段)
- 调用下一层处理
- 在下一层返回之后执行代码(响应阶段)
常见的中间件功能:日志记录、身份认证、CORS 处理、限流等。
代码示例
示例1:基础 Handler 使用
package main
import (
"fmt"
"log"
"net/http"
)
// 自定义 Handler 结构体
type GreetHandler struct {
Greeting string
}
// 实现 Handler 接口
func (h *GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, World!", h.Greeting)
}
// 使用 HandlerFunc 适配器
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "This is the about page.")
}
func main() {
// 创建自定义 Handler 实例
greet := &GreetHandler{Greeting: "Hello"}
// 注册路由
http.Handle("/greet", greet)
http.HandleFunc("/about", aboutHandler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
示例2:实现日志中间件
package main
import (
"log"
"net/http"
"time"
)
// 真正的业务处理器
func homeHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome to the homepage!"))
}
// 日志中间件:记录请求信息
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 调用下一层处理器
next.ServeHTTP(w, r)
// 记录日志
log.Printf(
"%s %s %s %v",
r.Method,
r.RequestURI,
r.RemoteAddr,
time.Since(start),
)
})
}
// 认证中间件示例
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 检查认证 token
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return // 注意:这里要 return,否则会继续执行
}
// 认证通过,继续处理
next.ServeHTTP(w, r)
})
}
func main() {
// 创建处理器
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
// 组合中间件:先认证,再日志,最后是业务处理器
// 注意顺序:请求是从外到内(auth -> logging -> home)
finalHandler := authMiddleware(loggingMiddleware(mux))
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", finalHandler))
}
实践练习
练习1:基础练习(易)
创建一个中间件,记录每个请求的处理时间(毫秒)。
// 你的代码实现
func timingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 记录开始时间
start := time.Now()
// 调用下一个处理器
// 计算并打印耗时
})
}
预期输出:控制台显示类似 请求处理耗时: 123µs 的日志
练习2:中等练习(中)
实现一个 IP 白名单中间件,只允许特定 IP 访问。
var allowedIPs = []string{"127.0.0.1", "::1"}
func ipWhitelistMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP := r.RemoteAddr // 注意:实际可能需要解析 IP 部分
// 检查 IP 是否在白名单中
// 如果不在,返回 403 Forbidden
// 如果在,调用 next.ServeHTTP
})
}
预期输出:非白名单 IP 访问时返回 403 错误
练习3:综合练习(难)
创建一个 "健康检查" 中间件链:
- 第一个中间件添加
X-Request-ID响应头(使用 UUID) - 第二个中间件记录请求到标准输出
- 最终处理器返回状态码 200 和 "OK"
预期输出:响应包含 X-Request-ID 头,且控制台有日志
常见错误
-
忘记调用
next.ServeHTTP()// 错误:中间件被中断,后续处理器不会执行 func badMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("before") // 缺少 next.ServeHTTP(w, r) log.Println("after") }) } -
中间件顺序错误
// 错误:认证应该在业务处理之前 finalHandler := loggingMiddleware(authMiddleware(mux)) // 错误顺序! // 正确:auth -> logging -> business finalHandler := authMiddleware(loggingMiddleware(mux)) -
在中间件中错误地结束响应
func middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if someCondition { http.Error(w, "error", 500) // 错误:没有 return,会继续执行 next } next.ServeHTTP(w, r) }) } -
误用
http.HandleFunc和http.Handle// 错误:HandleFunc 期望函数,Handle 期望 Handler http.Handle("/path", func(w http.ResponseWriter, r *http.Request) {}) // 编译错误 // 正确: http.HandleFunc("/path", func(w http.ResponseWriter, r *http.Request) {})
小结
- Handler 接口:
http.Handler是 Go Web 服务的核心,所有请求处理器都需要实现ServeHTTP方法 - HandlerFunc:通过
http.HandlerFunc适配器,普通函数可以直接用作 Handler - 中间件模式:通过包装 Handler 实现功能增强,遵循洋葱模型
- 中间件链:可以像俄罗斯套娃一样层层嵌套,形成处理链
- 常见应用:日志、认证、限流、CORS、压缩等都可以用中间件实现
下一课我们将学习如何设计规范的 REST API,利用今天学到的 Handler 和中间件知识构建生产级的 Web 服务。
练习编辑器
go
Loading...