在 Golang 中實現一個簡單的Http Middleware

本文主要針對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 相關的功能, 希望對您有用.

最後歡迎掃碼關注公衆號 【全球技術精選】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章