本文主要針對Golang的內建庫 net/http 做了簡單的擴充套件,通過新增中介軟體的形式實現了管道(Pipeline)模式,這樣的好處是各模組之間是低耦合的,符合單一職責原則,可以很靈活的通過中介軟體的形式新增一些功能到管道中,一次請求和響應在管道中的執行過程如下
首先, 我定義了三個測試的中介軟體 Middleware1,2,3 如下
func Middleware1(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M1 in")
next.ServeHTTP(w, r)
fmt.Println("M1 out")
})
}
func Middleware2(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M2 in")
next.ServeHTTP(w, r)
fmt.Println("M2 out")
})
}
func Middleware3(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("M3 in")
next.ServeHTTP(w, r)
fmt.Println("M3 out")
})
}
這裡中介軟體的入參和出參的型別都是 http.Handler, 然後在 next.ServeHTTP() 的前後分別輸出了 In 和 Out.
接下來,定義一個 Pipeline 的方法,裡面使用巢狀的形式, 使用了上面定義的三個測試的中介軟體.
func Pipeline(next http.Handler) http.Handler {
return Middleware1(Middleware2(Middleware3(next)))
}
然後還需要業務程式碼,這裡我定義了 LoginHandler 和 RegisterHandler 兩個方法
func LoginHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Login...")
w.Write([]byte("Login..."))
}
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("Register...")
w.Write([]byte("Register..."))
}
最後修改程式的 main 函式, 在 Login 介面上使用上面新增過中介軟體的 Pipeline
func main() {
http.Handle("/Login", Pipeline(http.HandlerFunc(LoginHandler)))
http.Handle("/Register", http.HandlerFunc(RegisterHandler))
http.ListenAndServe(":8080", nil)
}
啟動程式後,訪問 http://localhost:8080/Login, 程式的輸出如下,這和本文最上面的管道的流程圖是一致的,然後訪問 Register 介面, 控制檯沒有輸出資訊,當然也不會執行任何中介軟體。
現在已經實現了中介軟體的機制,但是,上面新增中介軟體是用巢狀的方法,這種方式不能說不太優雅,只能說非常的Low,接下來我們需要對管道進行優化
type Chain struct {
middlewares []func(handler http.Handler) http.Handler
}
func Pipeline(next http.Handler) http.Handler {
//return Middleware1(Middleware2(Middleware3(next)))
return AddMiddlewares(Middleware1,Middleware2,Middleware3).Then(next)
}
func AddMiddlewares(m ...func(handlerFunc http.Handler) http.Handler) Chain {
c := Chain{}
c.middlewares = append(c.middlewares,m...)
return c
}
func (c Chain) Then(next http.Handler) http.Handler {
for i := range c.middlewares {
prev := c.middlewares[len(c.middlewares)-1-i]
next = prev(next)
}
return next
}
首先定義了一個Chain 的struct,用來接收新增到管道中的中介軟體,在 AddMiddlewares() 函式中,接收了多個Handle, 然後組裝到 Chain 物件並返回, 接下來呼叫 Then() 函式, 把管道中的中介軟體和業務的Handler 關聯起來。在中介軟體的使用方式上, 這兩種方法都是一樣的,只需要呼叫 Pipeline() 方法就行了。
本文在go web中簡單的實現了中介軟體的機制,這樣帶來的好處也是顯而易見的,當然社群也有一些成熟的 middleware 元件,包括 Gin 一些Web框架中也包含了 middleware 相關的功能, 希望對您有用.
最後歡迎掃碼關注公眾號 【全球技術精選】