国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Golang > 正文

如何編寫Go語言中間件的實例教程

2020-04-01 18:57:47
字體:
來源:轉載
供稿:網友

引言

web開發的背景下,“中間件”通常意思是“包裝原始應用并添加一些額外的功能的應用的一部分”。這個概念似乎總是不被人理解,但是我認為中間件非常棒。

首先,一個好的中間件有一個責任就是可插拔并且自足。這就意味著你可以在接口級別嵌入你的中間件他就能直接運行。它不會影響你編碼方式,不是框架,僅僅是你請求處理里面的一層而已。完全沒必要重寫你的代碼,如果你想使用中間件的一個功能,你就幫他插入到那里,如果不想使用了,就可以直接移除。

縱觀Go語言,中間件是非常普遍的,即使在標準庫中。雖然開始的時候不會那么明顯,在標準庫net/http中的函數StripText或者TimeoutHandler就是我們要定義和的中間件的樣子,處理請求和相應的時候他們包裝你的handler,并處理一些額外的步驟。

一開始,我們認為編寫中間件似乎很容易,但是我們實際編寫的時候也會遇到各種各樣的坑。讓我們來看看一些例子。

1、讀取請求

在我們的示例中,所有的中間件都將接受http。處理程序作為一個參數,并返回一個http.Handler。這使得人們很容易就能把中間產品串起來。我們所有的中間產品的基本模式是這樣的:

func X(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Something here... h.ServeHTTP(w, r) })}

我們想要將所有的請求重定向到一個斜杠——比方說/message/,到它們的非斜杠等效,比如/message。我們可以這樣寫:

func TrailingSlashRedirect(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" && r.URL.Path[len(r.URL.Path)-1] == '/' { http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusMovedPermanently) return } h.ServeHTTP(w, r) })}

有沒有很簡單。

2、修改請求

比方說,我們想要向請求添加一個標題,或者修改它。http.Handler文檔中指明:

除了讀取主體之外,處理程序不應該修改所提供的請求。

Go標準庫復制http.Request。請求對象在將其傳遞到響應鏈之前,我們也應該這樣做。假設我們想在每個請求上設置Request-Id頭,以便內部跟蹤。創建*Request的淺副本,并在代理之前修改標題。

func RequestID(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r2 := new(http.Request) *r2 = *r r2.Header.Set("X-Request-Id", uuid.NewV4().String()) h.ServeHTTP(w, r2) })}

3、編寫響應頭

如果你想設置響應頭,你可以只寫它們,然后代理請求。

func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", servername) h.ServeHTTP(w, r) })}

上面的問題是,如果內部處理器也設置了服務器頭,那么你的頭將被覆蓋。如果不想公開內部軟件的服務器頭,或者如果想在發送響應給客戶端之前去掉頭部,這可能會帶來問題。

要做到這一點,我們必須自己實現ResponseWriter接口。大多數時候,我們只會代理到底層的ResponseWriter,但是如果用戶試圖寫一個響應,我們就會潛入并添加我們的標題。

type serverWriter struct { w http.ResponseWriter name string wroteHeaders bool}func (s *serverWriter) Header() http.Header { return s.w.Header()}func (s *serverWriter) WriteHeader(code int) http.Header { if s.wroteHeader == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } s.w.WriteHeader(code)}func (s *serverWriter) Write(b []byte) (int, error) { if s.wroteHeader == false { // We hit this case if user never calls WriteHeader (default 200) s.w.Header().Set("Server", s.name) s.wroteHeader = true } return s.w.Write(b)}

要在我們的中間件中使用它,我們會寫:

func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sw := &serverWriter{ w: w, name: servername, } h.ServeHTTP(sw, r) })}

問題

如果用戶從不調用Write或WriteHeader呢?例如,有一個200狀態并且是空body,或者對選項請求的響應——我們的攔截函數都不會運行。因此,我們應該在ServeHTTP調用之后再添加校驗。

func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sw := &serverWriter{ w: w, name: servername, } h.ServeHTTP(sw, r) if sw.wroteHeaders == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } })}

其他ResponseWriter接口

ResponseWriter接口只需要有三種方法。但在實踐中,它也可以對其他接口作出響應,例如http.Pusher。你的中間件可能會意外地禁用HTTP/2支持,這是不好的。

// Push implements the http.Pusher interface.func (s *serverWriter) Push(target string, opts *http.PushOptions) error { if pusher, ok := s.w.(http.Pusher); ok { return pusher.Push(target, opts) } return http.ErrNotSupported}// Flush implements the http.Flusher interface.func (s *serverWriter) Flush() { f, ok := s.w.(http.Flusher) if ok { f.Flush() }}

總結

通過以上的學習,不知道大家對Go編寫中間件有沒有一個完整的認識。大家也可以嘗試著用Go去編寫一個中間件。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 巴东县| 通许县| 广东省| 桑植县| 张家川| 芮城县| 珲春市| 修水县| 石景山区| 林周县| 通城县| 贡觉县| 台江县| 衢州市| 长宁县| 定兴县| 抚远县| 安新县| 漾濞| 咸宁市| 濉溪县| 扬中市| 平乡县| 景洪市| 五指山市| 曲阜市| 梅州市| 肃南| 韶关市| 客服| 屏南县| 旬邑县| 清苑县| 伽师县| 山西省| 鲁甸县| 沙湾县| 旅游| 即墨市| 新津县| 东安县|