Gorilla mux optional query values

stassinari picture stassinari · Jul 28, 2017 · Viewed 39.3k times · Source

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.

Answer

jmaloney picture jmaloney · Jul 28, 2017

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()))
}