
golang常用库:gorilla/mux-http路由库使用
发布日期:2021-05-09 04:00:31
浏览次数:16
分类:博客文章
本文共 9949 字,大约阅读时间需要 33 分钟。
一:golang自带路由介绍
golang自带路由库 http.ServerMux ,实际上是一个 map[string]Handler,是请求的url路径和该url路径对于的一个处理函数的映射关系。这个实现比较简单,有一些缺点:
- 不支持参数设定,例如/user/:uid 这种泛型类型匹配
- 无法很友好的支持REST模式,无法限制访问方法(POST,GET等)
- 也不支持正则
二:gorilla/mux路由
上面所指出来的glang自带路由的缺点, 都具备,而且还兼容 http.ServerMux。除了支持路径正则,命名路由,还支持中间件等等功能。所以mux是一个短小精悍,功能很全的路由。github地址:
1. 普通路由
示例 demo1.go
package mainimport ( "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标准库用法一样
在浏览器访问:输出:hello, Products2. 参数路由
参数路由,可以是普通路由,还可以是正则匹配
示例 demo2.go:package mainimport ( "net/http" "fmt" "github.com/gorilla/mux")//路由参数func main() { r := mux.NewRouter() //1. 普通路由参数 // r.HandleFunc("/articles/{title}", TitleHandler) //2. 正则路由参数,下面例子中限制为英文字母 r.HandleFunc("/articles/{title:[a-z]+}", TitleHandler) http.ListenAndServe(":8080", r)}//https://github.com/gorilla/mux#examplesfunc TitleHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) // 获取参数 w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "title: %v\n", vars["title"])}第1个普通路由参数,就是啥参数都可以,不管是字母,数字,还是中文等第2个正则路由参数,限制了只能是英文字母,否则会报
404 page not found
3. 路由匹配 Matching Routes
我们也可以限制路由或者子路由。3.1 匹配host
r := mux.NewRouter()//只匹配 www.example.comr.Host("www.example.com")// 动态匹配子路由r.Host("{subdomain:[a-z]+}.example.com")
3.2 更多的一些其他匹配
见下面的更多匹配的例子:
r := mux.NewRouter()r.PathPrefix("/products/") //前缀匹配r.Methods("GET", "POST") //请求方法匹配r.Schemes("https") //schemesr.Headers("X-Requested-With", "XMLHttpRequest") //header 匹配r.Queries("key", "value") //query的值匹配// 用户自定义方法 匹配r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { return r.ProtoMajor == 0})把上面的联合起来在一个单独的route里
r.HandleFunc("/products", ProductsHandler). Host("www.example.com"). Methods("GET"). Schemes("http")
3.3 子路由匹配
Subrouter() 可以设置子路由
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个路由都匹配了,那么优先匹配第一个路由。
r := mux.NewRouter()r.HandleFunc("/specific", specificHandler)r.PathPrefix("/").Handler(catchAllHandler)
4. 设置路由前缀
PathPrefix() 设置路由前缀
r := mux.NewRouter()//PathPrefix() 可以设置路由前缀product := r.PathPrefix("/products").HandleFunc("/", ProductsHandler)
路由前缀一般情况下不会单独使用,而是和子路由结合起来用,实现路由分组
5. 分组路由
可以根据前面的子路由和路由前缀的功能,综合运用就可以设置分组路由了
实例:grouprouter.gopackage mainimport ( "fmt" "github.com/gorilla/mux" "net/http")//子路由, 分组路由func main() { r := mux.NewRouter() //PathPrefix() 可以设置路由前缀,设置路由前缀为products products := r.PathPrefix("/products").Subrouter() //"http://localhost:8080/products/", 最后面的斜线一定要,不然路由不正确,页面出现404 products.HandleFunc("/", ProductsHandler) //"http://localhost:8080/products/{key}" products.HandleFunc("/{key}", ProductHandler) users := r.PathPrefix("/users").Subrouter() // "/users" users.HandleFunc("/", UsersHandler) // "/users/id/参数/name/参数" 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. 路由中间件
Mux middlewares are defined using the de facto standard type: 在mux中路由中间件的定义type MiddlewareFunc func(http.Handler) http.Handler
示例1:middleware1.go
package mainimport ( "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
在来看一个复杂点的例子:
package mainimport ( "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 mainimport ( "fmt" "net/http" "strings" "github.com/gorilla/mux")func handler(w http.ResponseWriter, r *http.Request) { return}//https://github.com/gorilla/mux#walking-routesfunc 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 mainimport ( "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() 方法来限制设置请求头的匹配。
demo4.gopackage mainimport ( "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)}
自定义匹配规
用 MatcherFunc() 来自定义规则
示例 demo5.go:**package mainimport ( "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)}在浏览器中:输出:FormValue: matcherfunc
命名路由 Registered URLs
namerouter.go
package mainimport ( "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}
根据命名的路由来获取路由URL r.Get("product").URL()
三:参考
发表评论
最新留言
关注你微信了!
[***.104.42.241]2025年03月29日 10时20分17秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
IntelliJ IDEA 中,项目文件右键菜单没有svn选项解决办法
2021-05-09
IDEA 调试Java代码的两个技巧
2021-05-09
深入理解JavaScript函数
2021-05-09
【spring源码系列】之【xml解析】
2021-05-09
(在模仿中精进数据可视化07)星球研究所大坝分布可视化
2021-05-09
(数据科学学习手札27)sklearn数据集分割方法汇总
2021-05-09
(数据科学学习手札40)tensorflow实现LSTM时间序列预测
2021-05-09
从零开始学安全(十六)● Linux vim命令
2021-05-09
阿里巴巴Json工具-Fastjson教程
2021-05-09
Spring Cloud Gateway - 快速开始
2021-05-09
Java对象转JSON时如何动态的增删改查属性
2021-05-09
Python 面向对象进阶
2021-05-09
Linux常用统计命令之wc
2021-05-09
Git安装及使用以及连接GitHub方法详解
2021-05-09
shell脚本里使用echo输出颜色
2021-05-09
并发编程——IO模型详解
2021-05-09
Java之封装,继承,多态
2021-05-09
wait()与notify()
2021-05-09
使用js打印时去除页眉页脚
2021-05-09