Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gorilla mux optional query values

Tags:

go

mux

gorilla

I've been working on a Go project where gorilla/mux is used as the router.

I need to be able to have query values associated with a route, but these values should be optional. That means that I'd like to catch both /articles/123 and /articles/123?key=456 in the same handler.

To accomplish so I tried using the r.Queries method that accepts key/value pairs: router.

  Path("/articles/{id:[0-9]+}").Queries("key", "{[0-9]*?}")

but this makes only the value (456) optional, but not the key. So both /articles/123?key=456 and /articles/123?key= are valid, but not /articles/123.

Edit: another requirement is that, after registering the route, I'd like to build them programatically, and I can't seem to work out how to use r.Queries even though the docs specifically state that it's possible (https://github.com/gorilla/mux#registered-urls).

@jmaloney answer works, but doesn't allow to build URLs from names.

like image 409
stassinari Avatar asked Jul 28 '17 16:07

stassinari


3 Answers

I would just register your handler twice.

router.Path("/articles/{id:[0-9]+}").
    Queries("key", "{[0-9]*?}").
    HandlerFunc(YourHandler).
    Name("YourHandler")

router.Path("/articles/{id:[0-9]+}").HandlerFunc(YourHandler)

Here is a working program to demonstrate. Notice that I am using r.FormValue to get the query parameter.

Note: make sure you have an up to date version go get -u github.com/gorilla/mux since a bug of query params not getting added the built URLs was fixed recently.

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

var router = mux.NewRouter()

func main() {
    router.Path("/articles/{id:[0-9]+}").Queries("key", "{key}").HandlerFunc(YourHandler).Name("YourHandler")
    router.Path("/articles/{id:[0-9]+}").HandlerFunc(YourHandler)

    if err := http.ListenAndServe(":9000", router); err != nil {
        log.Fatal(err)
    }
}

func YourHandler(w http.ResponseWriter, r *http.Request) {
    id := mux.Vars(r)["id"]
    key := r.FormValue("key")

    u, err := router.Get("YourHandler").URL("id", id, "key", key)
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    // Output:
    // /articles/10?key=[key]
    w.Write([]byte(u.String()))
}
like image 142
jmaloney Avatar answered Nov 20 '22 15:11

jmaloney


If you register query parameters they are required doc:

All variables defined in the route are required, and their values must conform to the corresponding patterns.

Because those parameters are optional you just need to check for them inside of a handler function: id, found := mux.Vars(r)["id"]. Where found will show if the parameter in the query or not.

like image 26
Pavlo Strokov Avatar answered Nov 20 '22 15:11

Pavlo Strokov


Seems like the best way to handle optional URL parameters is to define your router as normal without them, then parse the optional params out like this:

urlParams := request.URL.Query()

This returns a map that contains the URL parameters as Key/Value pairs.

like image 9
Nick Avatar answered Nov 20 '22 15:11

Nick