使用echo框架可以方便的定義自己的中間件,這裏研究下echo中間件的實現以及是如何實現鏈式調用的。
比如我們有下面的中間件:
func CalHandleTime(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
start := time.Now()
defer func() {
fmt.Println("請求處理時間:", time.Since(start) / time.MilliSecond)
}
err = next(c)
return
}
}
func main() {
e := echo.New()
e.Use(CalHandleTime)
...
}
該中間件計算了請求處理時間,CalHandleTime僅僅返回的是一個echo.HandlerFunc方法,此時HandlerFunc方法中的代碼並沒有被執行。
HandlerFunc的定義如下:
HandlerFunc func(Context) error
中間件方法(MiddlewareFunc)的定義就是傳入一個HandlerFunc,然後返回一個HandlerFunc:
MiddlewareFunc func(HandlerFunc) HandlerFunc
可以看下e.Use()的實現,就是把我們的中間件方法添加到中間件數組中,供後續調用:
func (e *Echo) Use(middleware ...MiddlewareFunc) {
e.middleware = append(e.middleware, middleware...)
}
我們定義自己的路由,然後使用這個中間件:
func main() {
e := echo.New()
e.GET("/hello", func(ctx echo.Context) error {
return ctx.String(http.StatusOK, "hello world~!")
}, CalHandleTime)
}
我們定義了一個Get方法的路由,Get方法第二個參數是具體的處理方法,我們簡單返回hello world,這個處理方法也是一個echo.HandlerFunc,可以發現是跟中間件返回的方法類型是一致的。Get方法後面的參數都是中間件參數,執行順序跟傳入順序一樣。
我們可以看下Get方法的實現:
func (e *Echo) add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) {
name := handlerName(handler)
e.router.Add(method, path, func(c Context) error {
h := handler
// 中間件調用鏈,這裏串聯起來了,合併成了一個方法。
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h(c)
})
r := &Route{
Method: method,
Path: path,
Handler: name,
}
e.router.routes[method+path] = r
}
我們主要看中間件部分的實現:
e.router.Add(method, path, func(c Context) error {
h := handler
// 中間件調用鏈,這裏串聯起來了,合併成了一個方法。
for i := len(middleware) - 1; i >= 0; i-- {
h = middleware[i](h)
}
return h(c)
})
- handler是我們傳入的處理業務的方法,就是返回hello world的那段代碼,是echo.HandlerFunc類型,middleware是我們傳入的中間件方法,可能有多個,所以這裏遍歷所有的,然後關聯起來,並且要保證順序。
- middleware類型是echo.MiddlewareFunc,可以看之前的定義,echo.MiddlewareFunc入參出參也都是echo.HandleFunc,跟我們處理業務邏輯的方法是一個類型,所以可以將業務處理方法和多箇中間件方法返回的echo.HandlerFunc合併成一個方法。
- 可以看到中間件方法入參是(next echo.HandlerFunc),也就是下一個echo.HandlerFunc,然後返回一個新的echo.HandlerFunc方法,新的方法封裝了自己的邏輯,然後再調用next echo.HandlerFunc,所以最後調用的中間件方法,裏面返回的echo.HandlerFunc會先執行。
- 可以看到遍歷中間件時採用了逆序的遍歷,也是因爲這個原因,這樣可以保證第一個定義的中間件方法中的echo.HandlerFunc會先執行,然後按照中間件定義的順序依次執行,最後執行我們的業務處理方法。這樣就合併成了一個方法,當請求到來時,會執行這個方法。
如果有表意不清或者錯誤的地方,請聯繫我糾正,謝謝。