中间件链接
该问题的常见解决方案是所谓的中间件链。有几个库提供此功能,例如negroni.
这是一种连续传递风格,您可以在其中编写中间件像这样的函数(取自 negroni 的自述文件):
func MyMiddleware(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// do some stuff before
next(rw, r)
// do some stuff after
}
然后 negroni 为您提供一个 HTTP 处理程序,以正确的顺序调用您的中间件。
我们可以稍微不同地实现这个解决方案,使其变得不那么神奇但更实用(如函数式编程) 方法。定义处理程序组合器如下:
func NewFooHandler(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// do some stuff before
next(r,w)
// do some stuff after
}
}
然后将您的链定义为组合:
h := NewFooHandler(NewBarHandler(NewBazHandler(Sink)))
Now h
is an http.HandlerFunc
依次执行 foo、bar、baz。Sink
只是一个空的最后一个处理程序,它不执行任何操作(“完成”链。)
将此解决方案应用于您的问题
定义一个处理程序组合器:
func NewResponseLoggingHandler(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// switch out response writer for a recorder
// for all subsequent handlers
c := httptest.NewRecorder()
next(c, r)
// copy everything from response recorder
// to actual response writer
for k, v := range c.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(c.Code)
c.Body.WriteTo(w)
}
}
现在问题归结为处理程序管理。您可能希望将此处理程序应用于某个类别中的所有链。为此,您可以再次使用组合器(这在某种程度上相当于 negroni 的Classic()
方法):
func NewDefaultHandler(next http.HandlerFunc) http.HandlerFunc {
return NewResponseLoggingHandler(NewOtherStuffHandler(next))
}
此后,每当您启动这样的链时:
h := NewDefaultHandler(...)
它将自动包含响应日志记录和您在中定义的所有默认内容NewDefaultHandler
.