新聞中心
在使用 Go 編寫復雜的服務時,您將遇到一個典型的主題是中間件。這個話題在網(wǎng)上被討論了一次又一次。本質上,中間件允許我們做了如下事情:

創(chuàng)新互聯(lián)基于成都重慶香港及美國等地區(qū)分布式IDC機房數(shù)據(jù)中心構建的電信大帶寬,聯(lián)通大帶寬,移動大帶寬,多線BGP大帶寬租用,是為眾多客戶提供專業(yè)四川聯(lián)通機房服務器托管報價,主機托管價格性價比高,為金融證券行業(yè)服務器托管,ai人工智能服務器托管提供bgp線路100M獨享,G口帶寬及機柜租用的專業(yè)成都idc公司。
- 攔截 ServeHTTP 調用,執(zhí)行任意代碼
- 對調用鏈(Continuation Chain) 上的請求/響應流進行更改
- 打斷中間件鏈,或繼續(xù)下一個中間件攔截器并最終到達真正的請求處理器
這些與 express.js 中間件所做的工作非常類似。我們探索了各種庫,找到了接近我們想要的現(xiàn)有解決方案,但是他們要么有不要的額外內容,要么不符合我們的品位。顯然,我們可以在 express.js 中間件的啟發(fā)下,寫出 20 行代碼以下的更清晰的易用的 API(Installation API)
抽象
在設計抽象時,我們首先設想如何編寫中間件函數(shù)(下文開始稱為攔截器),答案非常明顯:
- func NewElapsedTimeInterceptor() MiddlewareInterceptor {
- return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
- startTime := time.Now()
- defer func() {
- endTime := time.Now()
- elapsed := endTime.Sub(startTime)
- // 記錄時間消耗
- }()
- next(w, r)
- }
- }
- func NewRequestIdInterceptor() MiddlewareInterceptor {
- return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
- if r.Headers.Get("X-Request-Id") == "" {
- r.Headers.Set("X-Request-Id", generateRequestId())
- }
- next(w, r)
- }
- }
它們看起來就像 http.HandlerFunc,但有一個額外的參數(shù) next,該函數(shù)(參數(shù))會繼續(xù)處理請求鏈。這將允許任何人像編寫類似 http.HandlerFunc 的簡單函數(shù)一樣寫攔截器,它可以攔截調用,執(zhí)行所需操作,并在需要時傳遞控制權。
接下來,我們設想如何將這些攔截器連接到 http.Handler 或 http.HandlerFunc 中。為此,首先要定義 MiddlewareHandlerFunc,它只是 http.HandlerFunc 的一種類型。(type MiddlewareHandlerFunc http.HandlerFunc)。這將允許我們在 http.HandlerFunc 棧上之上構建一個更好的 API?,F(xiàn)在給定一個 http.HandlerFunc 我們希望我們的鏈式 API 看起來像這樣:
- func HomeRouter(w http.ResponseWriter, r *http.Request) {
- // 處理請求
- }
- // ...
- // 在程序某處注冊 Hanlder
- chain := MiddlewareHandlerFunc(HomeRouter).
- Intercept(NewElapsedTimeInterceptor()).
- Intercept(NewRequestIdInterceptor())
- // 像普通般注冊 HttpHandler
- mux.Path("/home").HandlerFunc(http.HandlerFunc(chain))
將 http.HandlerFunc 傳遞到 MiddlewareHandlerFunc,然后調用 Intercept 方法注冊我們的 Interceptor。Interceptor 的返回類型還是 MiddlewareHandlerFunc,它允許我們再次調用 Intercept。
使用 Intercept 組合需要注意的一件重要事情是執(zhí)行的順序。由于 chain(responseWriter, request)是間接調用最后一個攔截器,攔截器的執(zhí)行是反向的,即它從尾部的攔截器一直返回到頭部的處理程序。這很有道理,因為你在攔截調用時,攔截器應該要在真正的請求處理器之前執(zhí)行。
簡化
雖然這種反向鏈系統(tǒng)使抽象更加流暢,但事實證明,大多數(shù)情況下 s 我們有一個預編譯的攔截器數(shù)組,能夠在不同的 handlers 之間重用。同樣,當我們將中間件鏈定義為數(shù)組時,我們自然更愿意以它們執(zhí)行順序聲明它們(而不是相反的順序)。讓我們將這個數(shù)組攔截器稱為中間件鏈。我們希望我們的中間件鏈看起來有點像:
- // 調用鏈或中間件可以按下標的順序執(zhí)行
- middlewareChain := MiddlewareChain{
- NewRequestIdInterceptor(),
- NewElapsedTimeInterceptor(),
- }
- // 調用所有以 HomeRouter 結尾的中間件
- mux.Path("/home").Handler(middlewareChain.Handler(HomeRouter))
實現(xiàn)
一旦我們設計好抽象的概念,實現(xiàn)就顯得簡單多了
- package middleware
- import "net/http"
- // MiddlewareInterceptor intercepts an HTTP handler invocation, it is passed both response writer and request
- // which after interception can be passed onto the handler function.
- type MiddlewareInterceptor func(http.ResponseWriter, *http.Request, http.HandlerFunc)
- // MiddlewareHandlerFunc builds on top of http.HandlerFunc, and exposes API to intercept with MiddlewareInterceptor.
- // This allows building complex long chains without complicated struct manipulation
- type MiddlewareHandlerFunc http.HandlerFunc
- // Intercept returns back a continuation that will call install middleware to intercept
- // the continuation call.
- func (cont MiddlewareHandlerFunc) Intercept(mw MiddlewareInterceptor) MiddlewareHandlerFunc {
- return func(writer http.ResponseWriter, request *http.Request) {
- mw(writer, request, http.HandlerFunc(cont))
- }
- }
- // MiddlewareChain is a collection of interceptors that will be invoked in there index order
- type MiddlewareChain []MiddlewareInterceptor
- // Handler allows hooking multiple middleware in single call.
- func (chain MiddlewareChain) Handler(handler http.HandlerFunc) http.Handler {
- curr := MiddlewareHandlerFunc(handler)
- for i := len(chain) - 1; i >= 0; i-- {
- mw := chain[i]
- curr = curr.Intercept(mw)
- }
- return http.HandlerFunc(curr)
- }
因此,在不到 20 行代碼(不包括注釋)的情況下,我們就能夠構建一個很好的中間件庫。它幾乎是簡簡單單的,但是這幾行連貫的抽象實在是太棒了。它使我們能夠毫不費力地編寫一些漂亮的中間件鏈。希望這幾行代碼也能激發(fā)您的中間件體驗。
文章名稱:用20行代碼寫出清晰易用的Go中間件API
轉載來源:http://fisionsoft.com.cn/article/ccdhhps.html


咨詢
建站咨詢
