HttpRouter
倉庫地址: julienschmidt/httprouter: A high performance HTTP request router that scales well (github.com)
Gin 的路由組件采用的是HttpRouter,這同樣也是一個輕量,高性能的路由組件,整個組件只有三個.go文件,代碼十分的簡潔,其主要有以下特點。
一對一匹配:一個請求只能匹配到零個或一個路由,且有利於 SEO 優化。
路徑自動校正:隨意選擇喜歡的 URL 風格,就算多了或者少了一個斜槓,也會自動重定向。如果有大小寫錯誤的話,查找時也會忽略大小寫進行正確的重定向。
路由參數自動解析:只要給路徑段一個名稱,路由器就會把動態值傳遞給你。由於路由器的設計,路徑參數解析的佔用非常低廉。
零垃圾:在路由分配與調度的過程中,不會產生任何內存垃圾。
RefstfulAPI 支持:路由器的設計鼓勵合理的分層的 Restful API。
錯誤處理:可以設置一個錯誤處理器來處理請求中的異常,路由器會將其捕獲並記錄,然後重定向到錯誤頁面。
基本用法
就像是springboot一樣,一個函數綁定一個 URL 且對應一個處理器。
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprintf(w, "<h1>Hello World!")
}
func main() {
router := httprouter.New()
router.GET("/hello", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}隨後用瀏覽器或者任何的接口測試工具輸入127.0.0.1:8080,即可看到正確的內容,我們可以看到HttpRouter僅僅只是做了路由,實際上依舊是采用的net/http默認組件,gin也是如此,只不過封裝的相對而言要更深一點。
命名參數
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"log"
"net/http"
)
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "<h1>Hello World!")
}
func main() {
router := httprouter.New()
router.GET("/hello:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}這次的路由後面加了一個:name,name就是一個命名參數,可以通過httprouter.Params來訪問參數切片,可以通過索引或者ByName(name)來獲取參數。同樣的,你可以把http.handler和http.handlerFunc當作httprouter.handler來使用,路由本身實現了其接口,例如下方的例子。
func Hello(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
//params := r.Context().Value(httprouter.ParamsKey)也可以
fmt.Fprintf(w, "hello, %s!\n", params.ByName("name"))
}當方法綁定的路由是/user/:user,下面的幾種 URL 的匹配情況
/user/gordon match
/user/you match
/user/gordon/profile no match
/user/ no match你不能將/user/new和/user/:user注冊到同一個請求方法上,每一個請求方法應當是相互獨立的。
捕獲全部參數
第二種類型是捕獲全部參數,顧名思義,他們匹配一切,因此必須位於Pattern的尾部。
Pattern: /src/*filepath
/src/ match
/src/somefile.go match
/src/subdir/somefile.go matchHttpRouter的工作原理是構建大量的前綴樹,感興趣的可以了解:httprouter package - github.com/julienschmidt/httprouter - Go Packages。
OPTIONS & CORS
有些人可能會希望修改對於 OPTIONS 的自動響應並設置一些響應頭來適配 CORS 的預檢請求,這些需求可以通過使用Router.GlobalOPTIONShandler 來實現。
router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Access-Control-Request-Method") != "" {
// 設置CORS響應頭
header := w.Header()
header.Set("Access-Control-Allow-Methods", r.Header.Get("Allow"))
header.Set("Access-Control-Allow-Origin", "*")
}
// 適配狀態碼204
w.WriteHeader(http.StatusNoContent)
})NOT FOUND 處理器
TIP
可能需要關閉Router.HandleMethodNotAllowed,來避免一些問題。
router.NotFound = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
//你的邏輯
})基本校驗
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func BasicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
//獲取基本的身份憑據
user, password, hasAuth := r.BasicAuth()
if hasAuth && user == requiredUser && password == requiredPassword {
// 將請求委派給給予的處理器
h(w, r, ps)
} else {
// 否則請求認證
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
}
}
}
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Not protected!\n")
}
func Protected(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Protected!\n")
}
func main() {
user := "gordon"
pass := "secret!"
router := httprouter.New()
router.GET("/", Index)
router.GET("/protected/", BasicAuth(Protected, user, pass))
log.Fatal(http.ListenAndServe(":8080", router))
}