HTTP 中间件链
难度:⭐⭐ 中等
考点
- 函数作为一等公民
- 装饰器模式
- HTTP Handler 组合
- panic recovery
提示
- Chain 从后往前包装:最后一个中间件最先接触 handler
- Logger:在调用 next 前记录日志
- Recovery:defer + recover 捕获 panic
- Auth:检查 "Bearer xxx" 格式的 token
参考答案(Go)
点击展开参考答案
go
//go:build ignore
package answer
import (
"fmt"
"net/http"
"strings"
)
type Middleware func(http.Handler) http.Handler
func Chain(middlewares ...Middleware) Middleware {
return func(final http.Handler) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
final = middlewares[i](final)
}
return final
}
}
func Logger(logFn func(string)) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logFn(fmt.Sprintf("%s %s", r.Method, r.URL.Path))
next.ServeHTTP(w, r)
})
}
}
func Recovery() Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
}
func Auth(validToken string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
token := strings.TrimPrefix(auth, "Bearer ")
if token != validToken {
w.WriteHeader(http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}