上篇教程我们介绍了 gorilla/mux
路由的基本使用,这篇教程继续介绍它的更多匹配规则,实际上,它可能是一个比 Laravel 路由更加强大的存在。
1、限定请求方法
类似 Laravel 路由可以通过 Route::get
、Route::post
这种方式来限定 HTTP 请求方法,gorilla/mux
支持通过 Methods
方法来限定请求方法,我们可以通过链式调用将其应用到上篇教程定义的基础路由规则上:
代码语言:javascript复制r := mux.NewRouter()
r.HandleFunc("/hello/{name:[a-z] }", sayHelloWorld).Methods("GET", "POST")
r.Handle("/zh/hello/{name}", &HelloWorldHandler{}).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", r))
下面我们通过 cURL
在命令行测试路由访问,当我们试图对 http://localhost:8080/zh/hello/golang
发起 POST 请求时,结果为空,表示不支持该方法:
2、路由前缀
和 Laravel 路由一样,gorilla/mux
路由也支持路由前缀:
代码语言:javascript复制r.PathPrefix("/hello").HandlerFunc(sayHelloWorld)
不过,路由前缀通常不会单独使用,而是和子路由结合使用,从而实现对路由的分组。
3、域名匹配
此外,gorilla/mux
路由还支持域名匹配,这和 Laravel 路由的子域名路由功能非常相似,只需在原来的路由规则基础上追加 Host
方法调用并指定域名即可:
代码语言:javascript复制r.HandleFunc("/hello/{name:[a-z] }", sayHelloWorld).Methods("GET").Host("goweb.test")
这样一来,只有当请求 URL 的域名为 goweb.test
时才会匹配到对应路由映射:
当然,传入的域名参数值为子域名时,就是子域名匹配了:
代码语言:javascript复制r.Handle("/zh/hello/{name}", &HelloWorldHandler{}).Methods("GET").Host("zh.goweb.test")
效果和上面一样:
为了保证上述测试成功,需要在本地 hosts
文件中添加相应的域名映射:
127.0.0.1 goweb.test
127.0.0.1 zh.goweb.test
代码语言:javascript复制
限定 Scheme
gorilla/mux
路由支持通过 Schemes
方法设置 Scheme 匹配:
代码语言:javascript复制r.Handle("/zh/hello/{name}", &HelloWorldHandler{}).Methods("GET").Host("zh.goweb.test").Schemes("https")
这样一来,只有 HTTPS 请求才能访问对应路由,对于 HTTP 请求,会返回 404 错误:
4、限定请求参数
接下来的几个路由匹配规则是 Laravel 不支持的,我们可以在 gorilla/mux
路由定义中通过 Headers
方法设置请求头匹配,比如下面这个示例,请求头必须包含 X-Requested-With
并且值为 XMLHttpRequest
才可以访问指定路由 /request/header
:
代码语言:javascript复制r.HandleFunc("/request/header", func(w http.ResponseWriter, r *http.Request) {
header := "X-Requested-With"
fmt.Fprintf(w, "包含指定请求头[%s=%s]", header, r.Header[header])
}).Headers("X-Requested-With", "XMLHttpRequest")
这样做的意义是限定客户端只能通过 Ajax 请求访问该路由,测试命令如下:
除了请求头之外,还可以通过 Queries
方法限定查询字符串,比如下面这个示例,查询字符串必须包含 token
且值为 test
才可以匹配到给定路由 /query/string
:
代码语言:javascript复制r.HandleFunc("/query/string", func(w http.ResponseWriter, r *http.Request) {
query := "token"
fmt.Fprintf(w, "包含指定查询字符串[%s=%s]", query, r.FormValue(query))
}).Queries("token", "test")
这在一些需要访问令牌的请求中非常有用,可以规避掉无效的请求。测试命令如下:
在 Laravel 中,可以通过中间件完成类似的功能,不过 gorilla/mux
可以更早地规避这种非法请求。
5、自定义匹配规则
最后,gorilla/mux
路由支持通过 MatcherFunc
方法自定义路由匹配规则,在该方法中,可以获取到请求实例 request
,这样我们就可以拿到所有的用户请求信息,并对其进行判断,符合我们预期的请求才能匹配并访问该方法应用到的路由。
比如下面这个示例,我们限定只有来自 https://xueyuanjun.com
域名的请求才可以匹配到 /custom/matcher
路由:
代码语言:javascript复制r.HandleFunc("/custom/matcher", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "请求来自指定域名: %s", r.Referer())
}).MatcherFunc(func(request *http.Request, match *mux.RouteMatch) bool {
return request.Referer() == "https://xueyuanjun.com"
})
如果不是的话,会返回 404 响应:
6、路由分组
作为路由匹配进阶使用教程的收尾,我们来看下如何在 gorilla/mux
路由中实现路由分组和命名,以及根据命名路由生成对应的 URL。
首先来看路由分组,gorilla/mux
没有直接提供类似路由分组的术语,这里我们借鉴 Laravel 路由的表述,以方便理解。
在 gorilla/mux
中,可以基于子路由器(Subrouter)来实现路由分组的功能,具体使用时,还可以借助前面介绍的路由前缀和域名匹配来对不同分组路由进行特性区分。
下面,我们以文章增删改查为例,将文章相关路由规则划分到路由前缀为 /posts
的子路由中:
func listPosts(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "文章列表")
}
func createPost(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "发布文章")
}
func updatePost(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "修改文章")
}
func deletePost(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "删除文章")
}
func showPost(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "文章详情")
}
...
// 路由分组(基于子路由 路径前缀)
postRouter := r.PathPrefix("/posts").Subrouter()
postRouter.HandleFunc("/", listPosts).Methods("GET")
postRouter.HandleFunc("/create", createPost).Methods("POST")
postRouter.HandleFunc("/update", updatePost).Methods("PUT")
postRouter.HandleFunc("/delete", deletePost).Methods("DELETE")
postRouter.HandleFunc("/show", showPost).Methods("GET")
代码语言:javascript复制
这样,/posts
前缀会应用到后面所有基于 postRouter
子路由定义的路由规则上,并且针对不同的操作,我们还限定了对应的请求方法,我们可以像这样测试上述路由的访问:
如果上述路由是管理后台路由,还可以结合子域名做进一步划分:
代码语言:javascript复制
代码语言:javascript复制postRouter := r.PathPrefix("/posts").Host("admin.goweb.test").Subrouter()
这样一来,只有域名为 admin.goweb.test
时才可以访问对应路由,提高了安全性:
7、路由命名
最后我们来看一下 gorilla/mux
中的路由命名,和 Laravel 路由命名一样,也是通过 Name
方法在路由规则中指定:
代码语言:javascript复制postRouter := r.PathPrefix("/posts").Subrouter()
postRouter.HandleFunc("/", listPosts).Methods("GET").Name("posts.index")
postRouter.HandleFunc("/create", createPost).Methods("POST").Name("posts.create")
postRouter.HandleFunc("/update", updatePost).Methods("PUT").Name("posts.update")
postRouter.HandleFunc("/delete", deletePost).Methods("DELETE").Name("posts.delete")
postRouter.HandleFunc("/show/{id:[0-9] }", showPost).Methods("GET").Name("posts.show")
然后我们可以像下面这样根据上述路由命名生成与之对应的 URL:
代码语言:javascript复制
代码语言:javascript复制// 打印路由对应的 URL
indexUrl, _ := r.Get("posts.index").URL()
log.Println("文章列表链接:", indexUrl)
createUrl, _ := r.Get("posts.create").URL()
log.Println("发布文章链接:", createUrl)
showUrl, _ := r.Get("posts.show").URL("id", "1")
log.Println("文章详情链接:", showUrl)
打印结果如下:
gorilla/mux
路由也支持中间件,下篇教程,我们就来介绍如何基于 gorilla/mux
编写并应用路由中间件。