How do I pass arguments to my handler
Asked Answered
A

1

79

I am trying to pass my database object along to my handlers, instead of having a global object. But I don't know if this is possible, I'm using Gorilla Mux package, and I can see that it takes a closure as a second param.

// https://github.com/gorilla/mux/blob/master/mux.go#L174
// HandleFunc registers a new route with a matcher for the URL path.
// See Route.Path() and Route.HandlerFunc().
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
    *http.Request)) *Route {
    return r.NewRoute().Path(path).HandlerFunc(f)
}

Which then defines the params i can use, ideally i would like to have a third param like this.

// In my main
router.HandleFunc("/users/{id}", showUserHandler).Methods("GET")

func showUserHandler(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
    fmt.Fprintf(w, "We should fetch the user with id %s", vars["id"])
}

Is there a workaround? Or do I need a global db object? I am new to Go, so please explain a potential answer in detail.

Aboutship answered 6/10, 2014 at 7:53 Comment(2)
https://mcmap.net/q/263092/-best-practice-with-sessions-gorilla-sessions can give you some ideas too.Aureomycin
@Aureomycin I actually tried simonsdotnet.wordpress.com/2014/09/06/… but found that it did't work with Gorilla, as the http package just wants the ServeHTTP, but the Gorilla wants a func with specific params. I could be wrong ofc.Aboutship
A
142

Welcome to Go.

It is acceptable to have global variables and specially database objects.

However, there are few ways to workaround that if you prefer not to, for example you can create a struct and define your showHandler on it.

type Users struct {
    db *gorm.DB
}

func (users *Users) showHandler(w http.ResponseWriter, r *http.Request) {
    //now you can use users.db
}
func (users *Users) addHandler(w http.ResponseWriter, r *http.Request) {
    //now you can use users.db
}

// setup
users := &Users{db: createDB()}
router.HandleFunc("/users/{id}", users.showHandler).Methods("GET")
router.HandleFunc("/users/new", users.addHandler)
//etc

Another approach is creating a wrapper function:

db := createDB()
router.HandleFunc("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    showUserHandler(w, r, db)
}).Method("GET")
Aceydeucy answered 6/10, 2014 at 8:3 Comment(6)
Thank you OneOfOne. The idea was, that i wanted a more DI way of doing it, maybe i'm overthinking it. Which solution would you say is the cleanest?Aboutship
@Aboutship for a database connection, I'd say a global variable is fine. if you want specific variables that can only be used by a specific set of functions then the struct solution.Aceydeucy
@Aboutship also keep in mind that while most database handlers are safe for concurrency, everything else isn't, so if you set a variable in one handlers and get it in another, you will have to use some sort of locking.Aceydeucy
I'd go with the "wrap" method proposed. I've seen it commonly used to wrap the handler with "middleware" services.Breckenridge
@Aceydeucy How can we use closure like showHandler taking a user parameter and return func (w responseHandler, req r*http.Request) {}Bricker
I know it's some years ago, but I still some go programmers saying global is fine. To me, global is not fine. Pass your collaborators. Otherwise testing is a nightmare. (Yes you can do end2end but it takes ages)Menis

© 2022 - 2024 — McMap. All rights reserved.