Middleware bọc từ trong ra ngoài, nhưng khi request tới thì chạy từ ngoài vào trong, rồi phần sau lời gọi next chạy ngược lại — giống ngăn xếp.
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r) // đi sâu vào trong
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
h := logging(auth(rateLimit(handler)))
// vào: logging -> auth -> rateLimit -> handler
// ra: handler -> rateLimit -> auth -> loggingHệ quả thực tế:
- Middleware đo thời gian / recover panic phải ở ngoài cùng mới bao được toàn bộ.
- Middleware xác thực đặt trước middleware chỉ dành cho user đã đăng nhập; nếu đặt sau thì handler đã chạy rồi.
- Muốn truyền dữ liệu xuống dưới (user id, request id) thì gắn vào r = r.WithContext(context.WithValue(...)) rồi chuyển r mới cho next, vì *http.Request là bất biến theo quy ước.
- Muốn ghi log status code phải bọc http.ResponseWriter bằng struct riêng để bắt WriteHeader; cách chuẩn là nhúng interface gốc rồi override đúng method cần.
Hàm helper chain(h, mws...) duyệt slice ngược để thứ tự khai báo đọc từ trái sang phải đúng như thứ tự chạy.