golang第三方庫mux的實現(xiàn)
gorilla/mux
一:golang自帶路由介紹
golang 自帶路由庫 http.ServerMux
,實際上是一個 map[string][Handler]
,是請求的url路徑和該url路徑對于的一個處理函數(shù)的映射關(guān)系。這個實現(xiàn)比較簡單,有一些缺點:
- 不支持參數(shù)設(shè)定,例如
/user/:uid
這種泛型類型匹配 - 無法很友好的支持REST模式,無法限制訪問方法(POST,GET等)
- 也不支持正則
二:gorilla/mux路由
GitHub 地址:https://github.com/gorilla/mux
上面所指出來的glang自帶路由的缺點,gorilla/mux 都具備,而且還兼容 http.ServerMux。除了支持路徑正則,命名路由,還支持中間件等等功能。所以mux是一個短小精悍,功能很全的路由。
1. 普通路由
示例 demo1.go
package main import ( "fmt" "github.com/gorilla/mux" "net/http" ) func main() { r := mux.NewRouter() //普通路由 r.HandleFunc("/", IndexHandler) r.HandleFunc("/products", ProductsHandler) http.ListenAndServe(":8080", r) } func IndexHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "hello world") } func ProductsHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "hello, Products") }
上面mux的普通路由是不是似曾相識,跟golang標(biāo)準(zhǔn)庫用法一樣
在瀏覽器訪問:http://localhost:8080/products
輸出:hello, Products
2. 參數(shù)路由
參數(shù)路由,可以是普通路由,還可以是正則匹配
示例 demo2.go:
package main import ( "net/http" "fmt" "github.com/gorilla/mux" ) //路由參數(shù) func main() { r := mux.NewRouter() //1. 普通路由參數(shù) // r.HandleFunc("/articles/{title}", TitleHandler) //2. 正則路由參數(shù),下面例子中限制為英文字母 r.HandleFunc("/articles/{title:[a-z]+}", TitleHandler) http.ListenAndServe(":8080", r) } //https://github.com/gorilla/mux#examples func TitleHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) // 獲取參數(shù) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "title: %v\n", vars["title"]) }
第1個普通路由參數(shù),就是啥參數(shù)都可以,不管是字母,數(shù)字,還是中文等
第2個正則路由參數(shù),限制了只能是英文字母,否則會報 404 page not found
3. 路由匹配 Matching Routes
https://github.com/gorilla/mux#matching-routes
我們也可以限制路由或者子路由。
3.1 匹配host
r := mux.NewRouter() //只匹配 www.example.com r.Host("www.example.com") // 動態(tài)匹配子路由 r.Host("{subdomain:[a-z]+}.example.com")
3.2 更多的一些其他匹配
見下面的更多匹配的例子:
r := mux.NewRouter() r.PathPrefix("/products/") //前綴匹配 r.Methods("GET", "POST") //請求方法匹配 r.Schemes("https") //schemes r.Headers("X-Requested-With", "XMLHttpRequest") //header 匹配 r.Queries("key", "value") //query的值匹配 // 用戶自定義方法 匹配 r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { return r.ProtoMajor == 0 })
把上面的聯(lián)合起來在一個單獨的route里
r.HandleFunc("/products", ProductsHandler). Host("www.example.com"). Methods("GET"). Schemes("http")
3.3 子路由匹配
Subrouter() 可以設(shè)置子路由
r := mux.NewRouter() s := r.Host("www.example.com").Subrouter() s.HandleFunc("/products/", ProductsHandler) s.HandleFunc("/products/{key}", ProductHandler) s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
3.4 多個路由匹的順序
如果有多個路由添加到路由器里面,那么匹配順序是怎么樣?按照添加的先后順序匹配。比如有2個路由都匹配了,那么優(yōu)先匹配第一個路由。
r := mux.NewRouter() r.HandleFunc("/specific", specificHandler) r.PathPrefix("/").Handler(catchAllHandler)
4. 設(shè)置路由前綴
PathPrefix() 設(shè)置路由前綴
r := mux.NewRouter() //PathPrefix() 可以設(shè)置路由前綴 product := r.PathPrefix("/products").HandleFunc("/", ProductsHandler)
路由前綴一般情況下不會單獨使用,而是和子路由結(jié)合起來用,實現(xiàn)路由分組
5. 分組路由
可以根據(jù)前面的子路由和路由前綴的功能,綜合運用就可以設(shè)置分組路由了
實例:grouprouter.go
package main import ( "fmt" "github.com/gorilla/mux" "net/http" ) //子路由, 分組路由 func main() { r := mux.NewRouter() //PathPrefix() 可以設(shè)置路由前綴,設(shè)置路由前綴為products products := r.PathPrefix("/products").Subrouter() //"http://localhost:8080/products/", 最后面的斜線一定要,不然路由不正確,頁面出現(xiàn)404 products.HandleFunc("/", ProductsHandler) //"http://localhost:8080/products/{key}" products.HandleFunc("/{key}", ProductHandler) users := r.PathPrefix("/users").Subrouter() // "/users" users.HandleFunc("/", UsersHandler) // "/users/id/參數(shù)/name/參數(shù)" users.HandleFunc("/id/{id}/name/{name}", UserHandler) http.ListenAndServe(":8080", r) } func ProductsHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "%s", "products") } func ProductHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) //獲取路由的值 fmt.Fprintf(w, "key: %s", vars["key"]) } func UsersHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, " %s \r\n", "users handler") } func UserHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) //獲取值 id := vars["id"] name := vars["name"] fmt.Fprintf(w, "id: %s, name: %s \r\n", id, name) }
6. 路由中間件
https://github.com/gorilla/mux#middleware
Mux middlewares are defined using the de facto standard type: 在mux中路由中間件的定義
type MiddlewareFunc func(http.Handler) http.Handler
示例1:middleware1.go
package main import ( "fmt" "net/http" "github.com/gorilla/mux" ) func main() { r := mux.NewRouter() r.HandleFunc("/", handler) r.Use(loggingMiddleware) http.ListenAndServe(":8080", r) } func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { //Do stuff here fmt.Println(r.RequestURI) fmt.Fprintf(w, "%s\r\n", r.URL) // Call the next handler, which can be another middleware in the chain, or the final handler. next.ServeHTTP(w, r) }) } func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("handle middleware")) fmt.Println("print handler") }
示例2:middleware2.go
在來看一個復(fù)雜點的例子:
package main import ( "fmt" "net/http" "strings" "github.com/gorilla/mux" ) type authMiddleware struct { tokenUsers map[string]string } func (amw *authMiddleware) Populate() { amw.tokenUsers = make(map[string]string) amw.tokenUsers["000"] = "user0" amw.tokenUsers["aaa"] = "userA" amw.tokenUsers["05ft"] = "randomUser" amw.tokenUsers["deadbeef"] = "user0" } func (amw *authMiddleware) Middleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := strings.Trim(r.Header.Get("X-Session-Token"), " ") if token == "" { fmt.Fprintf(w, "token is error \r\n") } if user, found := amw.tokenUsers[token]; found { //we found the token in out map fmt.Printf("Authenticated user: %s\n", user) fmt.Fprintf(w, "Authenticated user: %s\n", user) // Pass down the request to the next middleware (or final handler) next.ServeHTTP(w, r) } else { // Write an error and stop the handler chain http.Error(w, "Forbidden", http.StatusForbidden) } }) } func main() { r := mux.NewRouter() r.HandleFunc("/", handler) amw := authMiddleware{} amw.Populate() r.Use(amw.Middleware) http.ListenAndServe(":8080", r) } func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("handler")) }
用 insomnia 軟件測試,如下圖:
X-Session-Token=aaa 返回時正確
那 -Session-Token=aaaa 呢
返回 403 了
7. Walking Routes 遍歷注冊的所有路由
package main import ( "fmt" "net/http" "strings" "github.com/gorilla/mux" ) func handler(w http.ResponseWriter, r *http.Request) { return } //https://github.com/gorilla/mux#walking-routes func main() { r := mux.NewRouter() r.HandleFunc("/", handler) r.HandleFunc("/products", handler).Methods("POST") r.HandleFunc("/articles", handler).Methods("GET") r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") r.HandleFunc("/authors", handler).Queries("surname", "{surname}") err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { pathTemplate, err := route.GetPathTemplate() if err == nil { fmt.Println("ROUTE:", pathTemplate) } pathRegexp, err := route.GetPathRegexp() if err == nil { fmt.Println("Path regexp:", pathRegexp) } queriesTemplates, err := route.GetQueriesTemplates() if err == nil { fmt.Println("Queries templates:", strings.Join(queriesTemplates, ",")) } queriesRegexps, err := route.GetQueriesRegexp() if err == nil { fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ",")) } methods, err := route.GetMethods() if err == nil { fmt.Println("Methods:", strings.Join(methods, ",")) } fmt.Println() return nil }) if err != nil { fmt.Println(err) } http.Handle("/", r) http.ListenAndServe(":8080", nil) }
8. 其他示例
請求方法限制
demo3.go:
package main import ( "fmt" "github.com/gorilla/mux" "net/http" ) // 請求方法的限制, Methods() func main() { r := mux.NewRouter() r.HandleFunc("/products", ProductsHandler).Methods("GET", "POST") r.Handle("/products/{id}", &ProductsIdHandler{}).Methods("GET") http.ListenAndServe(":8080", r) } func ProductsHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "hello, products! ") } type ProductsIdHandler struct{} func (handler *ProductsIdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "products id: %s", vars["id"]) }
請求頭限制
在路由定義中可以通過Headers() 方法來限制設(shè)置請求頭的匹配。
demo4.go
package main import ( "fmt" "net/http" "github.com/gorilla/mux" ) // 請求頭的限制,用Headers() 來限制 func main() { r := mux.NewRouter() r.HandleFunc("/products", func(w http.ResponseWriter, r *http.Request) { header := "Request-Limit-Test" fmt.Fprintf(w, "contain headers: %s = %s \n", header, r.Header[header]) }).Headers("Request-Limit-Test", "RequestLimitTest").Methods("POST") http.ListenAndServe(":8080", r) }
自定義匹配規(guī)
用 MatcherFunc() 來自定義規(guī)則
示例 demo5.go:**
package main import ( "fmt" "net/http" "github.com/gorilla/mux" ) //自定義匹配 MatcherFunc() func main() { r := mux.NewRouter() r.HandleFunc("/products/matcher", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "FormValue: %s ", r.FormValue("func")) }).MatcherFunc(func(req *http.Request, match *mux.RouteMatch) bool { b := false if req.FormValue("func") == "matcherfunc" { b = true } return b }) http.ListenAndServe(":8080", r) } 在瀏覽器中:http://127.0.0.1:8080/products/matcher?func=matcherfunc 輸出:FormValue: matcherfunc
命名路由 Registered URLs
namerouter.go
package main import ( "fmt" "github.com/gorilla/mux" // "log" "net/http" ) // 命名路由 Name(), 獲取路由URL, URL() func main() { r := mux.NewRouter() r.HandleFunc("/products/{category}/{id:[0-9]+}", ProductHandler).Name("product") //獲取路由的URL url1, err := r.Get("product").URL() fmt.Println(err) //error: mux: number of parameters must be multiple of 2, got [/] if err == nil { fmt.Println("get URL: \r\n", url1) } //獲取路由的url后,也可以拼裝你需要的URL url2, err := r.Get("product").URL("category", "tech", "id", "13") if err == nil { fmt.Println("new url: ", url2) //new url: /products/tech/13 } http.ListenAndServe(":8080", r) } func ProductHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) vars := mux.Vars(r) fmt.Fprintf(w, "url: %s, category: %s, id: %s", r.URL, vars["category"], vars["id"]) //瀏覽器: http://localhost:8080/products/id/23 //output //url: /products/id/23, category: id, id: 23 }
根據(jù)命名的路由來獲取路由URL r.Get(“product”).URL()
到此這篇關(guān)于golang第三方庫mux的實現(xiàn)的文章就介紹到這了,更多相關(guān)golang mux內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言標(biāo)準(zhǔn)庫中math模塊詳細(xì)功能介紹與示例代碼
Go語言的標(biāo)準(zhǔn)庫math提供了一系列基礎(chǔ)數(shù)學(xué)函數(shù)和常量,用于進行科學(xué)計算、幾何計算和其他數(shù)學(xué)相關(guān)的操作,這篇文章主要介紹了Go語言標(biāo)準(zhǔn)庫中math模塊詳細(xì)功能介紹與示例代碼,需要的朋友可以參考下2025-03-03淺析Go語言中的緩沖區(qū)及其在fmt包中的應(yīng)用
這篇文章主要為大家詳細(xì)介紹了Go語言中的緩沖區(qū)及其在fmt包中的應(yīng)用的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-01-01