天天看點

Go語言經典庫使用分析(六)| Negroni 中間件(二)

Go語言經典庫使用分析,未完待續,歡迎掃碼關注公衆号

flysnow_org

或者網站http://www.flysnow.org/,第一時間看後續系列。覺得有幫助的話,順手分享到朋友圈吧,感謝支援。

上一篇 Go語言經典庫使用分析(五)| Negroni 中間件(一) 中介紹了Negroni中間的入門使用和一些介紹,比如如何添加中間等,中間件的路由等。這一篇主要講原理,比如如何建構的中間處理鍊,如何編寫自己的中間件等。

Negroni Handler處理器

本質上來說

Negroni

是一個HTTP Handler,因為他實作了HTTP Handler接口,是以他可以被

http.ListenAndServe

使用,其次

Negroni

本身内部又有一套自己的Handler處理鍊,通過他們可以達到處理http請求的目的,這些Handler處理鍊中的處理器,就是一個個中間件。

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}           

複制

以上代碼,是

Negroni

實作了HTTP的Handler,這樣就和标注庫裡的http無縫整合了。實作了HTTP Handler,對于HTTP Request來說,就有一個統一的入口,所有的對HTTP Requet的處理,都會被

Negroni

通過

ServeHTTP

方法轉交給

Negroni

内部注冊的中間件。

type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)           

複制

這是

Negroni

自己定義的Handler處理器,它和HTTP Handler非常相似,唯一不同的是多了一個

next

參數,這個

next

參數是組成中間件處理鍊的核心。

如何建構中間件處理鍊

我們已經知道了

Negroni

有自己的一套Handler中間件處理鍊,那麼這個處理鍊和如何建構的呢?要想解開這個謎底,我們先看下

Negroni

如何注冊一個中間件的。

type Negroni struct {
	middleware middleware
	handlers   []Handler
}

func (n *Negroni) Use(handler Handler) {
	if handler == nil {
		panic("handler cannot be nil")
	}

	n.handlers = append(n.handlers, handler)
	n.middleware = build(n.handlers)
}           

複制

在我們調用

Use

方法的時候,會把

Negroni

的Handler存在自己的

handlers

字段中,這是一個Slice類型字段,可以儲存我們存放的Negroni Handler。同時會基于這個存放Negroni Handler的Slice建構中間件處理鍊

middleware

type middleware struct {
	handler Handler
	next    *middleware
}           

複制

middleware

struct有很簡單,有兩個字段,一個是目前的Negroni Handler,一個是指向下一個

middleware

的指針

next

。有了這樣一個

middleware

struct,就可以建構一個完美的中間件處理鍊了。

func build(handlers []Handler) middleware {
	var next middleware

	if len(handlers) == 0 {
		return voidMiddleware()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddleware()
	}

	return middleware{handlers[0], &next}
}

func voidMiddleware() middleware {
	return middleware{
		HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middleware{},
	}
}           

複制

以上代碼就是建構一個中間件處理鍊的邏輯,這是一個遞歸的函數,邏輯比較簡單。

handlers

參數為空的時候,直接通過

voidMiddleware

函數傳回一個空的

middleware

。 當

handlers

參數隻有1個處理器的時候,建構的

milldeware

就沒有

next

了,是以

next

是通過

voidMiddleware

函數獲得的,然後再通過

return middleware{handlers[0], &next}

組成生成的

middleware

并傳回。

最後一種情況,就是有1個以上的Negroni Handler,那就通過

build

函數循環遞歸了,從第2個Handler開始,不停的往後遞歸處理,是以最先被添加的Negroni Handler會被放在中間件處理鍊的前面,也就意味着會被優先執行。

中間件如何被調用

建構好了處理器鍊,那麼這些中間件如何被調用的呢?前面的章節,我們講了Negroni是一個HTTP Handler,是以總的入口在

ServeHTTP

方法裡。

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}           

複制

從以上代碼可以看出,調用了

middleware.ServeHTTP

方法,這就是中間件被執行的開始。

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}           

複制

middleware.ServeHTTP

方法調用

middleware

中目前handler的

ServeHTTP

方法執行我們自己寫的中間件處理邏輯。

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}           

複制

又回到這裡了吧,handler的

ServeHTTP

方法執行,本質上就是執行的我們自己定義的negroni.HandlerFunc。

目前的中間件被執行了,那麼下一個如何被觸發的?這就是我們自己定義的中間件函數中的next參數了。我們在自己的中間件處理結束後,如果覺得有必要,就需要調用next函數,繼續執行下一個中間件,如果我們不調用next函數,那麼中間件鍊的處理,到這裡就斷了。看一個自定義中間件的例子。

func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()

	next(rw, r)

	//省略無關代碼
}           

複制

這是Negroni内置的log中間件的實作,可以看到,它調用了

next(rw, r)

函數,讓中間件處理鍊繼續執行。這個

next(rw, r)

是什麼呢?其實前面我們講過。

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}           

複制

next

函數的調用,就是對下一個

middleware

ServeHTTP

的再次調用,這是一個遞歸,也是中間件處理鍊設計的巧妙之處,靈活控制,自由的選擇是否調用下個中間件。

Go語言經典庫使用分析,未完待續,歡迎掃碼關注公衆号

flysnow_org

或者網站http://www.flysnow.org/,第一時間看後續系列。覺得有幫助的話,順手分享到朋友圈吧,感謝支援。

http Handler和negroni Handler之間的轉換

在剛介紹Negroni的時候,我們知道,它是相容HTTP Handler的,Negroni可以直接把HTTP Handler轉換為Negroni HTTP,讓我們可以直接使用HTTP Handler作為Negroni的中間件,下面我們看下是如何轉換的。

func (n *Negroni) UseHandler(handler http.Handler) {
	n.Use(Wrap(handler))
}

func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
	n.UseHandler(http.HandlerFunc(handlerFunc))
}           

複制

Negroni提供兩個方法,分别相容

http.Handler

http.HandlerFunc

UseHandlerFunc

方法本質上還是調用的

UseHandler

方法,

UseHandler

方法實作的重點就是

Wrap(handler)

,它完成了http.Handler到negroni.Handler的轉換。

func Wrap(handler http.Handler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}           

複制

這個函數本質上是Negroni中間件的實作,它的代碼邏輯就是先執行

http.Handler

中間件的實作,然後調用

next(rw, r)

執行下一個中間件。

内置中間件介紹

Negroni内置了幾個中間件,當我們通過

Classic

函數建立一個

*Negroni

的時候,就會有

func Classic() *Negroni {
	return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}           

複制

NewRecovery

,

NewLogger

NewStatic

就是這幾個内置的中間件,都是實作了negroni handler而成的中間件,具體源代碼大家可以看下,這裡就不具體介紹了。

編寫自己的中間件

編寫自己的中間件,在Negroni可以采用兩種方式,一種是

http.Handler

的方式,這種方式優點是大家都熟悉,并且已經會了,缺點也有,就是不能控制中間件的處理鍊,預設是調用下一個中間件的,我們不能中斷。

另外一個就是實作

negroni.Handler

的方式,Negroni推薦的也是這種方式。

//Blog:www.flysnow.org
//Wechat:flysnow_org
func main() {
	n := negroni.New()
	n.UseFunc(printAuthorInfo)

	router:=http.NewServeMux()
	router.Handle("/",handler())

	n.UseHandler(router)

	n.Run(":1234")

}

func printAuthorInfo(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc){
	fmt.Println("Blog:www.flysnow.org")
	fmt.Println("Wechat:flysnow_org")
	next(rw,r)
}


func handler() http.Handler{
	return http.HandlerFunc(myHandler)
}

func myHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "text/plain")
	io.WriteString(rw,"Hello World")
}           

複制

我們通過

printAuthorInfo

函數實作了一個Negroni Handler中間件,這個中間件很簡單,隻是列印一些作者資訊,然後就執行下一個中間件。

使用的時候,我們通過

n.UseFunc(printAuthorInfo)

注冊這個中間件,我們啟動服務,通路

http://localhost:1234

的時候,就可以在控制台看到如下資訊:

Blog:www.flysnow.org
Wechat:flysnow_org           

複制

為了舉例,我這個中間件比較簡單,大家可以根據自己的需求實作滿足自己業務的中間件。

一些中間件介紹

Github上有很多專門為Negroni開發的第三方中間件,以配合Negroni的使用,比如gzip的,oauth2.0的等,大家可以通過如下連結檢視第三方的中間清單:

https://github.com/urfave/negroni#third-party-middleware

小結

到這裡,Negroni這個中間件分析完了,可能還有一些沒有分析的,比如

With

方法,

Run

方法,他們都是比較簡單的,大家看下就可以了。

還有一個比較重要的是

NewResponseWriter

這個函數,它是對我們現在的

NewResponseWriter

的包裝,增加了一些額外的資訊,這個知識點我在這個篇 Go語言經典庫使用分析(四)| Gorilla Handlers 源代碼實作分析 文章裡詳細介紹過,就不再重複介紹了。

HTTP的中間件,就是一個HTTP的攔截器,Negroni對于該攔截器處理的更好,可以靈活控制,是否終端等,合理的使用Negroni,可以讓你事半功倍。

Go語言經典庫使用分析,未完待續,歡迎掃碼關注公衆号

flysnow_org

或者網站http://www.flysnow.org/,第一時間看後續系列。覺得有幫助的話,順手分享到朋友圈吧,感謝支援。