Hello awesome Stackoverflow Community,
Apologies for the lame question. I was trying to find an answer to this question for some time, and googling didn't help me much.
I was playing around with Go's net/http
package and was following an article on organising *sql.Db
After going through it, I was trying to get two *sql.Db
types, one for master and one for slave. My intention was, for a simple route /home
, if the session
cookie is already set, use the master connection and let the connection go to the master server and use an RW user, and if not, use the slave connection with an RO user. I already have a wrapper struct DB
over *sql.DB
and a CheckSessionCookie method attached to it.
func (d *DB) CheckSessionCookie(w http.ResponseWriter, r *http.Request) (*http.Cookie) {
c, err := r.Cookie("session")
if err != nil {
return nil
}
return c
}
My routes looks like
http.HandleFunc("/admin", envMaster.Admin) // Master connection's method
http.HandleFunc("/home", envSlave.Home) // Slave connection's method
However, I cannot think of an elegant way to use a different handler func if the request has a cookie already set. I was thinking, if I could read the cookie with a javascript and pass the cookie as a parameter, and use the route as
http.HandleFunc("/home", envMaster.Home)
http.HandleFunc("/home/:session", envSlave.Home)
But I am not sure if that is a good way to do it, especially if http only
flag is set to the cookie.
How do I use a master connection if the request to "/home" has a session cookie already set?
Thank you.
One approach could be something like this:
type Env struct {
master *sql.DB
slave *sql.DB
}
func (e *Env) DB(r *http.Request) *sql.DB {
if _, err := r.Cookie("session"); err == http.ErrNoCookie {
return e.slave
}
return e.master
}
func (e *Env) Home(w http.ResponseWriter, r *http.Request) {
db := e.DB(r)
// ...
}
NOTE: The following is a presonal opinion and therefore not really SO material.
Although the example above may work for you, personally I think that tacking handlers on the Env
type is a poor design decision. If you have a tiny app with 3, 5, or 10 routes it may be ok, but if you need to scale to more than that, say 50 routes, you end up with a single type that has 50+ methods and while the methods may depend on the environment it's not really obvious, at least to me, why the environment itself should have behaviour defined on it.
Then there's also the possibility of different handlers requiring different configuration types, or same configuration types but with different values, not only does Env
now have 50+ methods it also has a multitude of fields that have little to nothing in common with each other. This also then allows a handler to touch stuff that it has no business touching.
In my opinion the Env
type should at most comprise a set of values that represent the environment in which the application is gonna run and that's it, no behaviour, or at least no more than mere predicates and getters that actually make sense as being part of the environment.