學習Golang的HTTP中介軟體機制
因為 Golang 內建的 net/http 天生就支援 HTTP 中介軟體機制,所以即便不用 gin 之類的 Web 框架,我們也可以寫出擴充套件性很好的 Web 應用。
假如你不瞭解 Golang 的 HTTP 中介軟體機制的話,那麼可以把它看成是一個洋蔥:

通過洋蔥看中介軟體
每一箇中間件都是一層洋蔥皮,其中每一箇中間件都可以改變請求和響應,我們可以很自然的把不同的業務邏輯放到不同的洋蔥皮裡,下面看看例子:
package main import ( "net/http" ) func foo(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("foo(")) next(w, r) w.Write([]byte(")")) } } func bar(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("bar(")) next(w, r) w.Write([]byte(")")) } } func test(w http.ResponseWriter, r *http.Request) { w.Write([]byte("test")) } func main() { http.Handle("/test", foo(bar(test))) http.ListenAndServe(":8080", nil) }
執行結果顯示如下,它形象的說明了中介軟體的執行過程:
foo(bar(test))
聯想一下洋蔥的結構,基本就能明白 Golang 的 HTTP 中介軟體機制了,不過不爽的是構建方式不易擴充套件,假如中介軟體很多的話,視覺上會呈現出複雜的巢狀,比如:
middleware(middleware(middleware(middleware(handler))))
我可不想維護這樣的程式碼,下面看看如何簡化編碼方式:
func main() { http.Handle("/test", new(foo).pipe(bar).process(test)) http.ListenAndServe(":8080", nil) } type middleware func(http.HandlerFunc) http.HandlerFunc type pipeline struct { middlewares []middleware } func new(ms ...middleware) pipeline { return pipeline{append([]middleware(nil), ms...)} } func (p pipeline) pipe(ms ...middleware) pipeline { return pipeline{append(p.middlewares, ms...)} } func (p pipeline) process(h http.HandlerFunc) http.HandlerFunc { for i := range p.middlewares { h = p.middlewares[len(p.middlewares)-1-i](h) } return h }
對比一下在修改程式碼前後呼叫方式發生了什麼改變,:
- 修改前:foo(bar(test))
- 修改後:new(foo).pipe(bar).process(test)
雖然表面上看好像程式碼更長了,但是對程式碼的可維護性而言,這並不是問題的關鍵,重要的是通過使用 Pipeline 模式,我們把原本巢狀結構改成了鏈式結構。