英文:
golang mux, routing wildcard & custom func match
问题
我正在使用mux包,它似乎工作得很好,只是它似乎不支持复杂的路由,或者至少我不知道它是如何支持的。
我有几个路由如下:
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product)
router.HandleFunc("/{user}", userHome)
router.HandleFunc("/search/price", searchPage)
所以我有两个问题:
-
如何定义一个通配符路由,例如 /search/price/*,以便匹配请求 /search/price/29923/rage/200/color=red?
-
是否可以为现有路由添加自定义条件?例如,如果路由是
/{productid}/{code}
,并且函数 x 返回true
,则使用handlerTrue
,如果返回false
,则使用handlerFalse
。
我尝试在路由中添加类似 .MatcherFunc(myfunction(ip)bool)
的内容,但它报错说路由器没有这样的方法。
目前,我在处理程序内部处理“自定义”条件。
英文:
I'm using the mux package which seems to work quite well except that it doesn't seem to support complex routes or at least I don't get it how it does.
I have several routes as following:
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product)
router.HandleFunc("/{user}", userHome)
router.HandleFunc("/search/price", searchPage)
So I have two questions:
-
How can I define a wildcard route such /search/price/* so that a request such /search/price/29923/rage/200/color=red can match it ?
-
Is it possible to add custom conditions to an existing route ? e.g. if the route is
/{productid}/{code}
and function x returnstrue
, use thishandlerTrue
, if it returnsfalse
usehandlerFalse
.
I've tried to add something like .MatcherFunc(myfunction(ip)bool)
to the route but it complains that the router has no such method.
Currently I'm handling the 'custom' conditions inside the handler.
答案1
得分: 12
你可以使用正则表达式。类似这样的写法:
router.HandleFunc(`/search/price/{rest:[a-zA-Z0-9=\-\/]+}`, searchPage)
这样,rest
将会捕获所有内容,所以在你的例子中,rest
将是 29923/rage/200/color=red
。你需要在代码中解析它。
不过,你可能想要一些可选参数。
router.HandleFunc(`/search{price:(\/price\/[0-9]+)?}{rage:(\/rage\/[0-9]+)?}{color:(\/color=[a-z]+)?}`, searchPage)
这样,你将得到变量 price = "/price/29923"
,rage = "/rage/200"
和 color = "/color=red"
,你仍然需要解析它们,但这样更容易,并且你可以控制哪些参数是有效的。如果你省略某个参数,它也能正常工作,例如 /search/price/29923/color=red
将只给出一个空的 rage
变量,但仍然匹配。
我不太明白你的第二个问题。
英文:
You can use regexps. Something like
router.HandleFunc(`/search/price/{rest:[a-zA-Z0-9=\-\/]+}`, searchPage)
That way, rest
will just capture everything, so in your example rest
would be 29923/rage/200/color=red
. You will need to parse that in your code.
You probably want some like optional arguments, though.
router.HandleFunc(`/search{price:(\/price\/[0-9]+)?}{rage:(\/rage\/[0-9]+)?}{color:(\/color=[a-z]+)?}`, searchPage)
After that, you get vars price = "/price/29923"
, rage = "/rage/200"
and color = "/color=red"
, that you still need to parse, but its easier, and you get to control which parameters are valid. It works as expected if you skip some parameter, eg. /search/price/29923/color=red
will just give an empty rage
variable, but still match.
I don't quite get your second question.
答案2
得分: 0
我不太确定你是否需要一个"通配符"路由:你只需要一个带有多个参数的路由即可:
/search/price/{price}/rage/{id}/color
这样的路由可以工作,注意查询字符串不需要包含在匹配器中(你可以通过 request.URL.Query
访问它们,而通过 mux.Vars
访问 mux 变量)。你还可以使用正则表达式来限定接受的参数。
另外,为了语义上的区分,你可以通过在用户路由和产品路由前加上 /user/{id}
和 /products/{id}/{code}
来区分它们。
至于 MatcherFunc
,你需要确保你的函数使用与 MatcherFunc 相同的签名(它是一个类型):func MatchIPAddresses(*http.Request, *RouteMatch) bool
可以解决这个问题。你可以通过检查 Request 结构体中的 r.RemoteAddr
或 r.Header.Get("X-Forwarded-For")
来访问 IP 地址,如果你期望在代理后面,可以使用后者。我通常会同时检查两者是否为空("")。
例如(简略示例;你可以稍作修改):
func MatchIPAddresses(r *http.Request, rm *RouteMatch) bool {
if r.RemoteAddr == "8.8.8.8" {
return true
} else if r.Header.Get("X-Forwarded-For") == "8.8.8.8" {
return true
}
return false
}
英文:
I'm not quite sure you need a "wildcard" route at all: you just need a route with multiple parameters:
/search/price/{price}/rage/{id}/color
will work, noting that query strings don't need to be included in the matcher (you access those via request.URL.Query
, whereas you access the mux variables via mux.Vars
. You can also use regex to narrow down the accepted parameters.
It will also help to differentiate your user and product routes, perhaps by prefixing them with /user/{id}
and /products/{id}/{code}
(particularly for semantics).
As far as MatcherFunc
goes, you need to make sure your function uses the same signature as MatcherFunc (which is a type): func MatchIPAddresses(*http.Request, *RouteMatch) bool
would solve it. You can access the IP address via the Request struct by checking r.RemoteAddr
or r.Header.Get("X-Forwarded-For")
if you expect to be behind a proxy. I typically check both if one is empty (""
).
i.e. (rough; you can clean this up a bit!)
func MatchIPAddresses(r *http.Request, rm *RouteMatch) bool {
if r.RemoteAddr == 8.8.8.8 {
return true
} else if r.Header.Get("X-Forwarded-For") == 8.8.8.8 {
return true
}
return false
}
答案3
得分: 0
要在gorilla mux中使用自定义的MatcherFunc,你需要确保你的匹配器实际上是mux.MatcherFunc类型。这是因为MatcherFunc不是一个接口类型。
// 来自mux/route.go的第303行
// MatcherFunc是自定义匹配器使用的函数签名。
type MatcherFunc func(*http.Request, *RouteMatch) bool
所以你需要这样做:
var myMatcher mux.MatcherFunc = func(request *http.Request, match *mux.RouteMatch) bool {
// 你的自定义逻辑
return trueOrFalse
}
// 然后你可以像这样在你的路由上使用它。
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product).MatcherFunc(myMatcher)
英文:
To use a custom MatcherFunc with gorilla mux, you need to ensure that your matcher is actually of type mux.MatcherFunc. This is because MatcheFunc is not an interface type
// From mux/route.go line 303
// MatcherFunc is the function signature used by custom matchers.
type MatcherFunc func(*http.Request, *RouteMatch) bool
So you have to do something like:
var myMatcher mux.MatcherFunc = func(request *http.Request, match *mux.RouteMatch) bool {
// Your custom logic
return trueOrFalse
}
// You can then use it on your route like this.
router := mux.NewRouter()
router.HandleFunc("/{productid}/{code}", product).MatcherFunc(myMatcher)
答案4
得分: -1
使用 chi 作为路由器,您可以执行以下操作:
由于正则表达式永远不会匹配斜杠 /
,您可以简单地使用 *
进行匹配。
例如,对于 /search/price/29923/rage/200/color=red
:
router.Get(`/search/price/*`, priceHandler)
func DashboardFilesHandler(w http.ResponseWriter, r *http.Request) {
path := myhandler.UrlParam(r, "*")
// path 将是 '29923/rage/200/color=red'
}
参见:https://godoc.org/github.com/go-chi/chi
使用名称后跟冒号的占位符允许进行正则表达式匹配,例如 {number:\d+}。正则表达式的语法是 Go 的正常 regexp RE2 语法,但不支持包含 { 或 } 的正则表达式,并且 / 永远不会 匹配。允许使用匿名正则表达式模式,在占位符的冒号之前使用空字符串,例如 {:\d+}。
**星号是特殊的占位符,它匹配请求 URL 的其余部分。**忽略模式中的任何尾随字符。这是唯一一个可以匹配 / 字符的占位符。
英文:
With chi as router you can do the following:
Since the regex never matches a slash /
you can simply match with *
e.g. for /search/price/29923/rage/200/color=red
router.Get(`/search/price/*`, priceHandler)
func DashboardFilesHandler(w http.ResponseWriter, r *http.Request) {
path := myhandler.UrlParam(r, "*")
// path will be '29923/rage/200/color=red'
}
See also: https://godoc.org/github.com/go-chi/chi
> A placeholder with a name followed by a colon allows a regular
> expression match, for example {number:\d+}. The regular expression
> syntax is Go's normal regexp RE2 syntax, except that regular
> expressions including { or } are not supported, and / will never be
> matched. An anonymous regexp pattern is allowed, using an empty string
> before the colon in the placeholder, such as {:\d+}
>
> The special placeholder of asterisk matches the rest of the requested
> URL. Any trailing characters in the pattern are ignored. This is the
> only placeholder which will match / characters.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论