go中函数级联实现责任链的核心在于安全传递、中断和改写请求响应:必须显式调用next.servehttp()才能流转,提前终止需writeheader+响应体+return三者齐全,跨中间件传值须用私有key的context,响应拦截需responsewriter包装器。

Go 里用函数级联实现责任链,不是为了炫技,而是为了在不侵入业务逻辑的前提下,把校验、日志、鉴权这些横切关注点拆开、可插拔、可复用。关键不在“链”本身,而在每个环节怎么安全地传递、中断、改写请求或响应。
中间件函数必须显式调用 next.ServeHTTP() 才能继续传递
这是最常漏的点:写了中间件,但没调 next.ServeHTTP(w, r),下游 handler 就永远收不到请求。Go 的 HTTP 处理器不会自动流转,全靠你手动控制。
- 漏掉这行,整个链就断在当前层,后续所有中间件和最终 handler 都不执行
- 如果想提前终止(比如鉴权失败),必须
return,否则next.ServeHTTP()还会执行,导致重复写 header - 调用
next.ServeHTTP()前后都可以加逻辑——比如耗时统计要放在调用后,日志打点通常放调用前
中断链时要确保响应已完整写出,避免 http: multiple response.WriteHeader calls
错误写法是只写 http.Error(w, "Unauthorized", http.StatusUnauthorized) 就结束,但没 return;或者写了 return 却没触发响应,让后续 handler 仍尝试写 header。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 正确中断 = 写完状态码 + 响应体 +
return(三者缺一不可) -
http.Error()内部已调用w.WriteHeader()和io.WriteString(),所以只需跟return - 自定义错误响应时,务必先
w.WriteHeader(status),再io.WriteString(w, body),最后return
跨中间件传数据必须用 context.Context,且 key 要私有类型
别用闭包捕获变量、别用全局 map、别直接改 *http.Request 字段——这些都会破坏 handler 的可复用性和并发安全性。
- 注入数据:用
r = r.WithContext(context.WithValue(r.Context(), key, value)),然后传给next.ServeHTTP() - 取数据:下游用
r.Context().Value(key),key 必须是私有类型(如type userIDKey struct{}),否则易冲突 - 修改请求体(如解密、重写 path):必须用
r.Clone(ctx)创建新请求,再传下去,原请求不可变
响应体拦截要用 ResponseWriter 包装器,不能只透传
想压缩响应、加统一 JSON 包装、记录响应大小,光靠中间件函数不行——http.ResponseWriter 是个接口,写操作发生在底层,必须用装饰器劫持 Write() 和 WriteHeader()。
- 直接
next.ServeHTTP(w, r)无法捕获响应内容,因为w是原始实例 - 需构造一个包装器(如
responseWriterWrapper),重写Write()缓存 body,WriteHeader()记录状态 - 注意:不要提前触发
WriteHeader(),否则后续Write()可能失效;缓存 body 时要考虑大文件场景,避免内存爆掉
真正难的不是写几个中间件,而是每层都得自己管 panic 恢复、context 传递、响应拦截、错误终止——Go 没给你兜底,它把选择权全交到你手上。链越长,这些细节漏一处,线上就可能出 500 或双写 header 的错。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










