内容简介:编写 go 中间件看起来挺简单的,但是有些情况下我们可能会遇到一些麻烦。让我们来看一些例子。
编写 go 中间件看起来挺简单的,但是有些情况下我们可能会遇到一些麻烦。
让我们来看一些例子。
读取请求
我们例子中的所有中间件都会接收一个 http.Handler
作为参数,并返回一个 http.Handler
。 这样便于将中间件链接起来。我们所有的中间件将会遵循如下基本模式:
func X(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Something here... // 这里还有一些其他信息 h.ServeHTTP(w, r) }) }
假设我们想要将所有请求重定向到一个末尾斜杠(例如, /messages/
), 这与重定向到它们的“非跟踪-斜杠”(比如 /messages
)是等价的。我们可以这么写 :
func TrailingSlashRedirect(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" && r.URL.Path[len(r.URL.Path)-1] == '/' { http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusMovedPermanently) return } h.ServeHTTP(w, r) }) }
如此简单。
修改请求
假设我们要向请求添加一个头部信息,或者修改它。 http.Handler
文档说明如下:
除了读取主体之外,处理程序不应修改所提供的请求。
Go 标注库在 传递 http.Request
对象到响应链之前会先拷贝 http.Request
,我们也应该这样做。 假设我们要为每个请求设置一个 Request-Id
头部信息,用于内部跟踪。 创建 *Request
的一个浅拷贝,并在代理之前修改头。
func RequestID(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r2 := new(http.Request) *r2 = *r r2.Header.Set("X-Request-Id", uuid.NewV4().String()) h.ServeHTTP(w, r2) }) }
写入响应头信息
如果你想设置响应头信息,你可以编写它们,然后代理请求。
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) }) }
使用最后写入的头部信息
上面写入响应头信息存在的问题是:如果内部处理程序也设置了服务器头部信息,你设置的响应头信息将被覆盖。 如果你不想公开内部软件的服务器头部信息,或者在向客户机发送响应之前要去掉头部,这可能会有问题。
为了应对这么问题,我必须自己实现 ResponseWriter
接口。 大多数情况下,我们会把它代理给潜在的 ResponseWriter
,但是如果用户试图写一个响应,我们会偷偷添加头部信息。
type serverWriter struct { w http.ResponseWriter name string wroteHeaders bool } func (s *serverWriter) Header() http.Header { return s.w.Header() } func (s *serverWriter) WriteHeader(code int) http.Header { 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) { if s.wroteHeader == false { // We hit this case if user never calls WriteHeader (default 200) // 如果用户从不调用 `WriteHeader`,我们就会遇到这种情况。 s.w.Header().Set("Server", s.name) s.wroteHeader = true } return s.w.Write(b) }
要将它用于我们的中间件,我们会这么写:
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, } h.ServeHTTP(sw, r)g }) }
如果用户从不调用 Write
或 WriteHeader
呢?
如果用户不调用 Write
或 WriteHeader
方法,比如一个状态码为 200 的空响应体,或者一个对可选请求的响应, 对于这些情况我们的拦截函数都不会被执行。 所以,鉴于这种情况我们应当在 ServeHTTP
调用之后添加至少一个检查。
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, } h.ServeHTTP(sw, r) if sw.wroteHeaders == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } }) }
其他 ResponseWriter
接口
ResponseWriter接口只需要实现三个方法。 但实际上,它也可以响应其他接口,例如 http.Pusher
。此外,你的中间件可能会意外禁用HTTP/2支持,这是不好的。
// Push implements the http.Pusher interface. // Push 实现 http.Pusher 接口 func (s *serverWriter) Push(target string, opts *http.PushOptions) error { if pusher, ok := s.w.(http.Pusher); ok { return pusher.Push(target, opts) } return http.ErrNotSupported } // Flush implements the http.Flusher interface. // Flush 实现 http.Flusher 接口 func (s *serverWriter) Flush() { f, ok := s.w.(http.Flusher) if ok { f.Flush() } }
就是这样
祝你好运!你正在写什么中间件呢,它们运行的怎样?
via: https://kev.inburke.com/kevin/how-to-write-go-middleware/
作者: Kevin Burke 译者: SergeyChang 校对: rxcai
本文由 GCTT 原创编译, Go 中文网 荣誉推出
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Producing Masculinity
Michele White / Routledge / 2019-3 / $39.95
Thoughtful, witty, and illuminating, in this book Michele White explores the ways normative masculinity is associated with computers and the Internet and is a commonly enacted online gender practice. ......一起来看看 《Producing Masculinity》 这本书的介绍吧!