golang mux, routing wildcard & custom func match
Asked Answered
L

4

9

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.

Lavinia answered 9/2, 2014 at 20:19 Comment(1)
The title should probably mention that it's gorilla mux, because there is mux in the go stdlib, so the title is a bit confusing.Starkey
S
12

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.

Seamanlike answered 10/2, 2014 at 23:57 Comment(5)
Sorry. For the second solution, which involves subexpressions, I think you'd need this fork of mux: github.com/gorilla/mux/pull/11 which is what I used in my last project.Pickford
the regex throws an error: "unknown escape sequence"Lavinia
Yeah, "" should be ``. Edited.Pickford
It doesn't throw out the error but the route is not handled either (I get 404 ) e.g. /search/price/0.000/asdasd/asdasdLavinia
The dot isn't matched, your initial example had no dot. Add it to the pattern: [a-zA-Z0-9=\-\/\.]+Pickford
E
0

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
}
Extent answered 9/2, 2014 at 22:36 Comment(3)
undefined: RouteMatch, cannot use myfunction(<T>, *RouteMatch) (type bool) as type mux.MatcherFunc in function argumentLavinia
I've fixed my example. I also highly recommend reading the Gorilla Mux docs, which have a similar example of how to write a MatcherFunc about halfway down the page: gorillatoolkit.org/pkg/muxExtent
I've read the docs before to post the question. I think your answer is basically copied from the docs except the ip function.Lavinia
A
0

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)
Aten answered 22/5, 2019 at 6:57 Comment(0)
D
-1

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.

Deuteranopia answered 9/5, 2018 at 8:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.