假设我在Go中有一个中间件,我想Server用自己的值覆盖任何现有的标头。
Server
// Server attaches a Server header to the response. func Server(h http.Handler, serverName string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", serverName) h.ServeHTTP(w, r) }) }
然后我将其添加到这样的响应链中
http.Handle("/", authHandler) http.ListenAndServe(":8000", Server(http.DefaultServeMux, "myapp"))
不幸的是,如果authHandler或由DefaultServeMux调用处理的任何事情w.Header().Add("Server", "foo")(例如,httputil.ReverseProxy.ServeHTTP),我最终在响应中会有两个Server标头。
w.Header().Add("Server", "foo")
$ http localhost:5962/v1/hello_world HTTP/1.1 200 OK Content-Length: 11 Content-Type: text/plain Date: Tue, 12 Jul 2016 04:54:04 GMT Server: inner-middleware Server: myapp
我真正想要的是这样的:
// Server attaches a Server header to the response. func Server(h http.Handler, serverName string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h.ServeHTTP(w, r) w.Header().Set("Server", serverName) }) }
但是,ServeHTTP的语义不允许这样做:
ServeHTTP应该将回复标头和数据写入ResponseWriter,然后返回。返回信号表明请求已完成;在ServeHTTP调用完成之后或与之同时使用ResponseWriter或从Request.Body中读取是无效的。
我已经看到一些与此有关的丑陋骇客,例如httputil.ReverseProxy.ServeHTTP中的代码,该代码复制标头并重写响应代码,或者使用httptest.ResponseRecorder将整个正文读入字节缓冲区。
我也可以颠倒传统的中间件顺序,并以某种方式将Server中间件放在最后,或者使其成为最内部的中间件。
我缺少任何简单的方法吗?
您可以定义一个自定义类型,该类型将包装ResponseWriter并在写入所有标头之前插入Server标头,但要付出额外的间接层费用。这是一个例子:
type serverWriter struct { w http.ResponseWriter name string wroteHeader bool } func (s serverWriter) WriteHeader(code int) { if s.wroteHeader == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } s.w.WriteHeader(code) } func (s serverWriter) Write(b []byte) (int, error) { return s.w.Write(b) } func (s serverWriter) Header() http.Header { return s.w.Header() } // Server attaches a Server header to the response. func Server(h http.Handler, serverName string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sw := serverWriter{ w: w, name: serverName, wroteHeader: false, } h.ServeHTTP(sw, r) }) }
我在这里写了更多有关此的内容。https://kev.inburke.com/kevin/how-to-write-go-middleware/