Skip to content

HTTP 中间件链 ​

难度:⭐⭐ 中等 ​

考点 ​

  • 函数作为一等公民
  • 装饰器模式
  • HTTP Handler 组合
  • panic recovery

提示 ​

  1. Chain 从后往前包装:最后一个中间件最先接触 handler
  2. Logger:在调用 next 前记录日志
  3. Recovery:defer + recover 捕获 panic
  4. 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)
		})
	}
}

持续学习,持续构建。