中间件链接
一个常见的解决方案是所谓的 中间件链
。有几个提供此功能的库,例如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)))
现在
h是一个
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。



