Key into map with undefined integer - go

I'm using Gorilla mux for my handlers and using mux.Vars. I'm trying to write a test for one of the handlers that uses mux.Vars so what I do is
var vars = map[string]string{
"id": user.ID,
}
context.Set(req, 0, vars)
In mux the key (an integer) is undefined so by default 0. I've logged the key when mux.Vars gets called and it prints 0. I should be able to key into this map
map[0:map[id:522d14f5b1b92235d6000002]]
by doing map[key] but that returns nil. However, I get the correct value back if I hardcode map[0]. Any thoughts?

I'm not entirely sure I understand the question, but it looks like you might be confusing mux.Vars with mux.context. The two are separate entities. The former returns route variables that are parsed from the URL path. For instance, you could do:
r := mux.NewRouter()
r.HandleFunc("/blah/{foo}/", MyHandler)
...
func MyHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
...
}
The latter contains context variables you set yourself. For instance:
func MyHandler(w http.ResponseWriter, r *http.Request) {
context.Set(r, 0, map[string]string{"id": "myid"})
myMap := context.Get(r, 0)
...
}
You might check out some usage examples of how others use both to see what is most appropriate for your use case:
mux.Vars: https://sourcegraph.com/github.com/gorilla/mux/symbols/go/github.com/gorilla/mux/Vars
mux.context: https://sourcegraph.com/github.com/gorilla/context/symbols/go/github.com/gorilla/context

Related

defining function inside of function

When defining a inner function which utilizes the variables of outer scope, should I pass the variables to the inner function as parameters?
In my example, generate and generate2 both give me same result, is there a reason I should choose any one of them?
The code picks key 1 to generate combinations with key 3,4,5,
then picks key 2 to generate combinations with key 3,4,5.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
src := map[int][]string{
1: []string{"1", "11", "111"},
2: []string{"2", "22"},
3: []string{"3"},
4: []string{"4"},
5: []string{"5", "55"},
}
result2 := generate2(src)
fmt.Println(result2)
result := generate(src)
fmt.Println(result)
}
func generate(src map[int][]string) []string {
var combo []string
var add = func(f []string) {
for _, v := range f {
for _, p := range src[3] {
for _, q := range src[4] {
for _, r := range src[5] {
combo = append(combo, v+p+q+r)
}
}
}
}
}
add(src[1])
add(src[2])
return combo
}
func generate2(src map[int][]string) []string {
var combo []string
var add = func(f []string, combo []string, src map[int][]string) []string {
for _, v := range f {
for _, p := range src[3] {
for _, q := range src[4] {
for _, r := range src[5] {
combo = append(combo, v+p+q+r)
}
}
}
}
return combo
}
combo = add(src[1], combo, src)
combo = add(src[2], combo, src)
return combo
}
When defining a inner function which utilizes the variables of outer scope, should I pass the variables to the inner function as parameters?
It depends on what you want to achieve.
What you call "a function inside a function" is actually called "a closure" (and some people call it "lambda").
Closures capture variables from the outer lexical scope, referenced in its body. In Go, this capturing is done "by reference" or "by name" which basically means each time a closure is called it will "see" current values of the variables it closes over, not the values these variables had at the time the closure was created—observe that the program:
package main
import (
"fmt"
)
func main() {
i := 42
fn := func() {
fmt.Println(i)
}
fn()
i = 12
fn()
}
would output
42
12
Conversely, when you pass values as arguments to calls to a closure, each call will see exactly the values passed to it.
I hope you now see that what strategy to pick largely depends on what you want.
Conceptually, you may think of a closure as being an instance of an ad-hoc anonymous struct data type, the fields of which are pointers to the variables the closure closes over, and each call to that closure being analogous to calling some (anonymous, sole) method provided by that type (actually, that's what the compiler usually does behind your back to implement a closure).
Such "method" may have arguments, and whether it should have them, and what should go to the type's fields and what should be that method's arguments can be judged using the usual approach you employ with regular types.
In this context, there is no functional difference between the two functions. As you noticed, local functions have access to local variables without explicitly passing them. In your example you might prefer to use generate1 for easier reading.

Gorilla mux optional query values

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

Register multiple routes using range for loop slices/map

Consider I have slice of string paths:
paths := []string{"/path0", "/path1", "/path2" /*... "/path-n"*/ }
// where n is the last path
Using package net/http, I want to register handler for this path using for loop with range clause. This is how I do this:
for _, path := range paths {
http.HandleFunc(path, handler)
}
// in this case every handler is print the path to the console or to the browser
EDIT: Basically the asker used this code:
for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}
But I ended up with same output which is the last element of slice, so when I go to /path1, the output is /path-n. Same behavior with other element, always print /path-n.
But if I use this:
http.HandleFunc(paths[0], handler)
http.HandleFunc(paths[1], handler)
http.HandleFunc(paths[2], handler)
// ...
http.HandleFunc(paths[n], handler)
The output is correct.
What's going on, did I miss something? I need for loop for registration given by slice of paths or map, so I can't do the second code.
Can you give me the alternative to accomplished this task?
So the problem was that you actually used this code:
for _, path := range paths {
http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path)
})
}
You used a function literal, a closure as the handler function to register. Closures capture the context they refer to, in your case the path loop variable.
But there is only a single path loop variable, its value is overwritten in each iterations of the loop, and its final value will be the last path. Relevant section from the spec: For statements with range clause:
The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement; they are re-used in each iteration. If the iteration variables are declared outside the "for" statement, after execution their values will be those of the last iteration.
Once the for loop is finished, and you start making requests, each registered handler function will send back the value of this single path variable. That's why you see the last path returned for all requested paths.
Solution is easy: create a new variable in each iteration, and use that in the handler function:
for _, path := range paths {
path2 := path
http.HandleFunc(path2, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, path2)
})
}
What happens here is that we use a short variable declaration in each iteration to create a new variable, initialized with the value of the path loop variable. And the handler function we register will refer to this new variable, unique only to one registered path.
Another, equally good solution is to use an anonymous function with a parameter to pass the path string. Might be harder to understand though:
for _, path := range paths {
func(p string) {
http.HandleFunc(p, func(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, p)
})
}(path)
}
What happens here is that we call an anonymous function, passing the current path value to it, and it registers the handler function, using only the parameter of this anonymous function (and there's a new, distinct local variable allocated for each call).

Meaning of underscore in a Go function parameter

Came accross the below function here. I noticed the last parameter is identified with _. What is the intent of this pattern?
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
It means "ignore that parameter", the reason that they still need the last parameter here is because they want to pass it as type Handle to the function GET, which has the signature:
type Handle func(http.ResponseWriter, *http.Request, Params)
If you simply pass something like func Index(w http.ResponseWriter, r *http.Request) it will not be treated as type Handle.
_ is the blank identifier. It's in the signature to show that the value doesn't get used, so the signature will still match the methods of the interface.
Using "_" in place of a parameter name fulfills the obligations of a higher-level "function as a parameter" without getting a warning about an unused parameter. In your case, I believe the compiler is told to ignore all incoming "POST" data, thus in effect reducing the request to the functionality of a "GET".
As others pointed out, it is a blank identifier. For instance, consider the following example:
func main() {
nums := []int{5, 3, 4}
max := nums[0]
for _, num := range nums {
if num > max {
max = num
}
}
fmt.Println("max:", max)
}
If you aren't going to use an index value, you can just ignore storing it by using _ instead of a variable name.=.

mux.Vars not working

I'm running on HTTPS (port 10443) and use subroutes:
mainRoute := mux.NewRouter()
mainRoute.StrictSlash(true)
mainRoute.Handle("/", http.RedirectHandler("/static/", 302))
mainRoute.PathPrefix("/static/").Handler(http.StripPrefix("/static", *fh))
// Bind API Routes
apiRoute := mainRoute.PathPrefix("/api").Subrouter()
apiProductRoute := apiRoute.PathPrefix("/products").Subrouter()
apiProductRoute.Handle("/", handler(listProducts)).Methods("GET")
And the functions:
func listProducts(w http.ResponseWriter, r *http.Request) (interface{}, *handleHTTPError) {
vars := mux.Vars(r)
productType, ok := vars["id"]
log.Println(productType)
log.Println(ok)
}
ok is false and I have no idea why. I'm doing a simple ?type=model after my URL..
When you enter a URL like somedomain.com/products?type=model you're specifying a query string, not a variable.
Query strings in Go are accessed via r.URL.Query - e.g.
vals := r.URL.Query() // Returns a url.Values, which is a map[string][]string
productTypes, ok := vals["type"] // Note type, not ID. ID wasn't specified anywhere.
var pt string
if ok {
if len(productTypes) >= 1 {
pt = productTypes[0] // The first `?type=model`
}
}
As you can see, this can be a little clunky as it has to account for the map value being empty and for the possibility of a URL like somedomain.com/products?type=model&this=that&here=there&type=cat where a key can be specified more than once.
As per the gorilla/mux docs you can use route variables:
// List all products, or the latest
apiProductRoute.Handle("/", handler(listProducts)).Methods("GET")
// List a specific product
apiProductRoute.Handle("/{id}/", handler(showProduct)).Methods("GET")
This is where you would use mux.Vars:
vars := mux.Vars(request)
id := vars["id"]
Hope that helps clarify. I'd recommend the variables approach unless you specifically need to use query strings.
An easier way to solve this is to add query parameters in your route through Queries, like:
apiProductRoute.Handle("/", handler(listProducts)).
Queries("type","{type}").Methods("GET")
You can get it using:
v := mux.Vars(r)
type := v["type"]
NOTE: This might not have been possible when the question was originally posted but I stumbled across this when I encountered a similar problem and the gorilla docs helped.

Resources