golang mux,路由通配符和自定义函数匹配

huangapple go评论83阅读模式
英文:

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 returns true , use this handlerTrue, if it returns false use handlerFalse.

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.RemoteAddrr.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.

huangapple
  • 本文由 发表于 2014年2月10日 04:19:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/21664489.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定