自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用 20 行代碼寫出清晰易用的 Go 中間件 API

新聞 前端
在使用 Go 編寫復(fù)雜的服務(wù)時(shí),您將遇到一個(gè)典型的主題是中間件。這個(gè)話題在網(wǎng)上被討論了一次又一次。

 [[331358]]

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

  • 攔截 ServeHTTP 調(diào)用,執(zhí)行任意代碼
  • 對(duì)調(diào)用鏈(Continuation Chain) 上的請(qǐng)求/響應(yīng)流進(jìn)行更改
  • 打斷中間件鏈,或繼續(xù)下一個(gè)中間件攔截器并最終到達(dá)真正的請(qǐng)求處理器

這些與 express.js 中間件所做的工作非常類似。我們探索了各種庫(kù),找到了接近我們想要的現(xiàn)有解決方案,但是他們要么有不要的額外內(nèi)容,要么不符合我們的品位。顯然,我們可以在 express.js 中間件的啟發(fā)下,寫出 20 行代碼以下的更清晰的易用的 API(Installation API)

抽象

在設(shè)計(jì)抽象時(shí),我們首先設(shè)想如何編寫中間件函數(shù)(下文開始稱為攔截器),答案非常明顯:

  1. func NewElapsedTimeInterceptor() MiddlewareInterceptor { 
  2. return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 
  3. startTime := time.Now() 
  4. defer func() { 
  5. endTime := time.Now() 
  6. elapsed := endTime.Sub(startTime) 
  7. // 記錄時(shí)間消耗 
  8. }() 
  9.  
  10. next(w, r) 
  11.  
  12. func NewRequestIdInterceptor() MiddlewareInterceptor { 
  13. return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { 
  14. if r.Headers.Get("X-Request-Id") == "" { 
  15. r.Headers.Set("X-Request-Id", generateRequestId()) 
  16.  
  17. next(w, r) 

它們看起來就像 http.HandlerFunc,但有一個(gè)額外的參數(shù) next,該函數(shù)(參數(shù))會(huì)繼續(xù)處理請(qǐng)求鏈。這將允許任何人像編寫類似 http.HandlerFunc 的簡(jiǎn)單函數(shù)一樣寫攔截器,它可以攔截調(diào)用,執(zhí)行所需操作,并在需要時(shí)傳遞控制權(quán)。

接下來,我們?cè)O(shè)想如何將這些攔截器連接到 http.Handler 或 http.HandlerFunc 中。為此,首先要定義 MiddlewareHandlerFunc,它只是 http.HandlerFunc 的一種類型。(type MiddlewareHandlerFunc http.HandlerFunc)。這將允許我們?cè)?http.HandlerFunc 棧上之上構(gòu)建一個(gè)更好的 API?,F(xiàn)在給定一個(gè) http.HandlerFunc 我們希望我們的鏈?zhǔn)?API 看起來像這樣:

  1. func HomeRouter(w http.ResponseWriter, r *http.Request) { 
  2. // 處理請(qǐng)求 
  3.  
  4. // ... 
  5. // 在程序某處注冊(cè) Hanlder 
  6. chain := MiddlewareHandlerFunc(HomeRouter). 
  7. Intercept(NewElapsedTimeInterceptor()). 
  8. Intercept(NewRequestIdInterceptor()) 
  9.  
  10. // 像普通般注冊(cè) HttpHandler 
  11. mux.Path("/home").HandlerFunc(http.HandlerFunc(chain)) 

將 http.HandlerFunc 傳遞到 MiddlewareHandlerFunc,然后調(diào)用 Intercept 方法注冊(cè)我們的 Interceptor。Interceptor 的返回類型還是 MiddlewareHandlerFunc,它允許我們?cè)俅握{(diào)用 Intercept。

使用 Intercept 組合需要注意的一件重要事情是執(zhí)行的順序。由于 chain(responseWriter, request)是間接調(diào)用最后一個(gè)攔截器,攔截器的執(zhí)行是反向的,即它從尾部的攔截器一直返回到頭部的處理程序。這很有道理,因?yàn)槟阍跀r截調(diào)用時(shí),攔截器應(yīng)該要在真正的請(qǐng)求處理器之前執(zhí)行。

簡(jiǎn)化

雖然這種反向鏈系統(tǒng)使抽象更加流暢,但事實(shí)證明,大多數(shù)情況下 s 我們有一個(gè)預(yù)編譯的攔截器數(shù)組,能夠在不同的 handlers 之間重用。同樣,當(dāng)我們將中間件鏈定義為數(shù)組時(shí),我們自然更愿意以它們執(zhí)行順序聲明它們(而不是相反的順序)。讓我們將這個(gè)數(shù)組攔截器稱為中間件鏈。我們希望我們的中間件鏈看起來有點(diǎn)像:

  1. // 調(diào)用鏈或中間件可以按下標(biāo)的順序執(zhí)行 
  2. middlewareChain := MiddlewareChain{ 
  3. NewRequestIdInterceptor(), 
  4. NewElapsedTimeInterceptor(), 
  5.  
  6. // 調(diào)用所有以 HomeRouter 結(jié)尾的中間件 
  7. mux.Path("/home").Handler(middlewareChain.Handler(HomeRouter)) 

實(shí)現(xiàn)

一旦我們?cè)O(shè)計(jì)好抽象的概念,實(shí)現(xiàn)就顯得簡(jiǎn)單多了

  1. package middleware 
  2.  
  3. import "net/http" 
  4.  
  5. // MiddlewareInterceptor intercepts an HTTP handler invocation, it is passed both response writer and request 
  6. // which after interception can be passed onto the handler function. 
  7. type MiddlewareInterceptor func(http.ResponseWriter, *http.Request, http.HandlerFunc) 
  8.  
  9. // MiddlewareHandlerFunc builds on top of http.HandlerFunc, and exposes API to intercept with MiddlewareInterceptor. 
  10. // This allows building complex long chains without complicated struct manipulation 
  11. type MiddlewareHandlerFunc http.HandlerFunc 
  12.  
  13.  
  14. // Intercept returns back a continuation that will call install middleware to intercept 
  15. // the continuation call. 
  16. func (cont MiddlewareHandlerFunc) Intercept(mw MiddlewareInterceptor) MiddlewareHandlerFunc { 
  17. return func(writer http.ResponseWriter, request *http.Request) { 
  18. mw(writer, request, http.HandlerFunc(cont)) 
  19.  
  20. // MiddlewareChain is a collection of interceptors that will be invoked in there index order 
  21. type MiddlewareChain []MiddlewareInterceptor 
  22.  
  23. // Handler allows hooking multiple middleware in single call. 
  24. func (chain MiddlewareChain) Handler(handler http.HandlerFunc) http.Handler { 
  25. curr := MiddlewareHandlerFunc(handler) 
  26. for i := len(chain) - 1; i >= 0; i-- { 
  27. mw := chain[i] 
  28. curr = curr.Intercept(mw) 
  29.  
  30. return http.HandlerFunc(curr) 

因此,在不到 20 行代碼(不包括注釋)的情況下,我們就能夠構(gòu)建一個(gè)很好的中間件庫(kù)。它幾乎是簡(jiǎn)簡(jiǎn)單單的,但是這幾行連貫的抽象實(shí)在是太棒了。它使我們能夠毫不費(fèi)力地編寫一些漂亮的中間件鏈。希望這幾行代碼也能激發(fā)您的中間件體驗(yàn)。

責(zé)任編輯:張燕妮 來源: Go語(yǔ)言中文網(wǎng)
相關(guān)推薦

2013-12-12 10:55:21

2023-11-27 07:10:06

日志中間件

2015-12-21 14:56:12

Go語(yǔ)言Http網(wǎng)絡(luò)協(xié)議

2021-10-06 19:03:35

Go中間件Middleware

2022-11-18 07:54:02

Go中間件項(xiàng)目

2021-04-29 21:54:44

Python代碼語(yǔ)言

2024-05-06 12:30:51

Go語(yǔ)言中間件

2024-10-06 13:49:30

2016-11-11 21:00:46

中間件

2021-02-11 08:21:02

中間件開發(fā)CRUD

2011-05-24 15:10:48

2023-12-06 07:14:28

前端API中間件

2017-12-11 13:30:49

Go語(yǔ)言數(shù)據(jù)庫(kù)中間件

2018-07-29 12:27:30

云中間件云計(jì)算API

2018-02-01 10:19:22

中間件服務(wù)器系統(tǒng)

2024-02-06 14:05:00

Go中間件框架

2013-03-13 10:37:22

中間件Windows

2018-05-02 16:23:24

中間件RPC容器

2024-08-09 08:11:02

2015-02-07 21:52:45

PaaS中間件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)