成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

提升Go的HTTP路由器的提案

譯文 精選
開發 前端
這篇文章介紹了一個提升 Go 標準庫中的 HTTP 路由器 http.ServeMux 的功能和性能的提案。該提案旨在讓 ServeMux 支持匹配 HTTP 方法和通配符路徑,從而滿足大多數 REST 風格的 API 服務器的需求。文章還對比了其他流行的第三方路由器庫,以及討論了該提案的設計決策和實現細節。

譯者 | 劉汪洋

審校 | 重樓

Go 的標準庫中包含一個穩定且成熟的 HTTP 服務器。然而,內置的請求路由器http.ServeMux 功能較為簡潔,因此你常常需要自己編寫路由代碼。

其主要短板是,它并未支持 HTTP 方法的匹配(如GET和POST的區別),同時也無法持/users/{user}/settings這種類型的通配符路徑。然而,這兩個功能幾乎是所有 REST 風格的 API 服務器所必需的。

當然,你可以選擇自行實現這些功能。在我以前的一篇文章 Go 中不同的 HTTP 路由方法中,提到過有一些優秀的第三方包可以實現更高級的路由功能,并且只需 約 30 行代碼 就能夠在不借助任何第三方庫的情況下實現類似的功能。

但是,未來可能不再需要這些替代方案和第三方包。現在有一個 活躍的提案 - 還包括一個旨在改進 ServeMux 參考實現 ,使其能夠匹配 HTTP 方法和通配符路徑。

Google 的 Go 團隊成員 Jonathan Amsterdam 主導了這個提案以及之前的 討論。Jonathan 曾成功提出將結構化日志添加到標準庫的提案 - Go 1.21 將包含他的log/slog包(預計 2023 年 8 月發布)。

現狀與變革

在目前的情況下,如果想將 GET 請求匹配到 /users/{user}/settings,你需要編寫以下的樣板代碼(盡管在實踐中你可能會使用第三方庫):

mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
        return
    }
    remainder := r.URL.Path[len("/users/"):]
    userId, subPath, _ := strings.Cut(remainder, "/")
    switch subPath {
    case "settings":
        fmt.Fprintf(w, "user %s", userId)
    // 其他子路徑可以在這里添加
    default:
        http.NotFound(w, r)
    }
})

如果接受了這個提議,你可以更加簡單地實現一樣的功能:

mux.HandleFunc("GET /users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "user %s", r.PathValue("user"))
})

這樣的寫法明顯更為簡潔!

這與其他流行路由器使用的語法非常相似:

// github.com/go-chi/chi
router.Get("/users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "user %s", chi.URLParam(r, "slug"))
})

// github.com/gorilla/mux
router.HandleFunc("/users/{user}/settings", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "user %s", mux.Vars(r)["user"])
}).Methods("GET")

// github.com/bmizerany/pat
router.Get("/users/:user/settings", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "user %s", r.URL.Query().Get(":user"))
}))

// github.com/gin-gonic/gin
router.GET("/users/:user/settings", func(c *gin.Context) {
    fmt.Fprintf(w, "user %s", c.Param("user"))
})

提案中的一個有趣決定是,并沒有為 ServeMux 添加新的方法;而是對現有的 Handle  HandleFunc 方法進行了擴展,以支持方法前綴和 {wildcard} 路徑段。

我理解他們避免添加新方法的想法,但我對這個決定持保留態度。遺憾的是,舊版的 ServeMux 接受如 Handle("GET /foo", h) 的模式。這意味著為增強版 ServeMux 編寫的代碼將在舊版 Go 上能正常編譯和運行,但路由不會匹配到任何內容,這容易導致錯誤。我可能會添加新的方法,比如 HandleMatch / HandleMatchFunc 或 Route / RouteFunc。

該提議也詳細描述了處理兩個重疊模式的優先級,其核心規則簡單明了:“如果兩個模式有重疊(有共同的請求),則更具體的模式優先匹配”。

例如,如果你注冊了模式 /users/(匹配 /users/*)以及模式 /users/{user},當一個 /users/ben 的請求進來時,它將匹配第二個,更具體的模式。這與現有的 ServeMux 中,特定主機的模式優先于沒有主機名的模式的行為一致。

URL 末尾通配符匹配

此提案為我們帶來了一個新的"特殊通配符" {$},它專門用于匹配 URL 的末尾。對于那些僅希望匹配主頁路由的情況,這個新特性顯得非常實用。在此之前,要實現這一目標頗為麻煩,因為以 / 結尾的模式會匹配所有 / 之下的內容;這個規則對于只有 / 的模式同樣適用。

因此,以前若想匹配主頁,你需要這樣操作:

mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" { // 確保路徑就是 "/"
        http.NotFound(w, r)
        return
    }
    serveHomepage(w, r)
})
mux.HandleFunc("/users", serveUsers)

這一過程頗為繁瑣。若你忘記了路徑檢查,那么你最終可能會將主頁用于所有其他的 URL,而不是顯示一個未找到的頁面,因為所有的內容都在 / 之下。

而根據新的提案,這個過程將變得更加簡潔:

mux.HandleFunc("/{$}", serveHomepage)
mux.HandleFunc("/users", serveUsers)

實現參考

Jonathan 在 github.com/jba/muxpatterns 中發布了一個 ServeMux 的增強版本的示例實現。唯一的區別在于,由于它是在單獨的包中,無法改變 http.Request 類型, 所以你需要用 mux.PathValue(request, "name") 來獲取路徑值,而非 request.PathValue("name")。

我在 我的 go-routing 倉庫 中添加了一個 PR,這個 PR 提供了我自己的 widget API 的一種實現,使用 muxpatterns。這個版本與 chi 版本 非常相似 —— 清晰且易讀:

r.HandleFunc("GET /{$}", home)
r.HandleFunc("GET /contact", contact)
r.HandleFunc("GET /api/widgets", apiGetWidgets)
r.HandleFunc("POST /api/widgets", apiCreateWidget)
r.HandleFunc("POST /api/widgets/{slug}", apiUpdateWidget)
r.HandleFunc("POST /api/widgets/{slug}/parts", apiCreateWidgetPart)
r.HandleFunc("POST /api/widgets/{slug}/parts/{id}/update", apiUpdateWidgetPart)
r.HandleFunc("POST /api/widgets/{slug}/parts/{id}/delete", apiDeleteWidgetPart)
r.HandleFunc("GET /{slug}", widgetGet)
r.HandleFunc("GET /{slug}/admin", widgetAdmin)
r.HandleFunc("POST /{slug}/image", widgetImage)

當我首次測試這個參考實現時,我發現了一些小問題,現已得到修復。

結論

盡管我對于擴展現有的 Handle 和 HandleFunc 方法有一些保留,但我對這個提案的考慮感到欣慰。鑒于 Jonathan 在提案中的謹慎處理、他在 log/slog 上的良好表現以及社區的積極反饋,此提案被接受的可能性很高。

如果這個功能能進入標準庫,那將非常棒 —— 我開發的幾乎所有網站和 REST 風格的 API 都將用到這個功能。Go 的標準庫已經非常強大,但加入這個功能將進一步減少對第三方路由器的依賴。

如果這個功能能夠集成在 2024 年 2 月發布的 Go 1.22 中,我并不會感到驚訝。讓我們拭目以待!

譯者介紹

劉汪洋,51CTO社區編輯,昵稱:明明如月,一個擁有 5 年開發經驗的某大廠高級 Java 工程師,擁有多個主流技術博客平臺博客專家稱號。

原文標題:The proposal to enhance Go’s HTTP router,作者:Ben Hoyt

責任編輯:華軒 來源: 51CTO
相關推薦

2009-12-22 14:33:51

2013-10-24 09:43:39

路由器

2010-08-03 13:28:57

2011-08-29 13:04:09

路由器設置路由器連接路由器

2011-08-11 13:49:24

2009-12-22 13:21:59

存儲路由器

2010-08-20 09:16:53

路由器基礎

2009-12-30 10:01:00

低端路由器高端路由器

2010-08-16 11:14:25

路由器綜合對比

2010-08-19 10:43:40

路由器體系結構

2017-02-21 09:50:17

2024-11-19 09:10:19

迭代器Go語言

2009-11-10 10:10:01

華為路由器

2013-07-05 09:28:21

軟路由路由技術

2009-12-11 15:21:15

華為路由器CISCO路由器

2011-02-22 08:57:28

路由器基礎

2011-05-04 15:56:15

路由器網吧

2009-04-10 09:34:00

網絡路由器分類

2009-12-16 16:25:14

路由器配置

2009-12-08 15:02:48

路由器技術
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲成av人片在线观看 | 亚洲精品视频播放 | 国产精品观看 | 欧美成人在线免费 | 亚洲a一区二区 | 美女天堂在线 | 一本一道久久a久久精品蜜桃 | 成人免费大片黄在线播放 | 欧美在线a| 91麻豆精品国产91久久久更新资源速度超快 | 国产精品99久久久久久动医院 | 久国产视频 | 久草免费福利 | 国产乱码精品一区二区三区五月婷 | 亚洲一区二区中文字幕 | 91就要激情 | 欧美特级黄色 | 中文字幕三区 | 久久精品免费一区二区 | 精品视频一区二区 | 欧美极品一区二区 | 精品久久久久久久人人人人传媒 | 美女福利网站 | 奇米av| 在线资源视频 | 99婷婷| 国产精品乱码一区二三区小蝌蚪 | 久久久91 | 亚洲高清免费视频 | 天堂网av在线 | 成人免费激情视频 | 人操人人 | 精品国产视频在线观看 | 超级黄色一级片 | 亚洲最大av网站 | 久久久综合网 | 欧美日韩久 | 国产精品美女久久久免费 | 久久综合av| 日韩第一区 | 国产a区|