Reputation: 10813
func indexHandler(w http.ResponseWriter, req *http.Request) {
session, err := store.Get(req, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
}
err = views.ExecuteTemplate(w, "index.html", session.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
All my handlers use Gorilla sessions. How can I avoid store.Get
ting the session in each of my handlers, i.e. code repeating?
Another issue, is there a better way to give the template the session values, other that an explicit way like:
err = views.ExecuteTemplate(w, "index.html",
struct {
Session map[interface{}]interface{},
...other handler specific data for the template
}{
session.Values,
...
})
Code samples originate from https://github.com/kaihendry/internal-google-login
Upvotes: 1
Views: 3264
Reputation: 38203
For the middleware you can define a function that takes a handler as an argument and returns another handler as its result.
func sessionMiddleware(h http.HandlerFunc) http.HandlerFunc {
// ...
}
The returned handler can store.Get
the session, if it's not present return the error, if it is present store the session into the request's context and then call the actual handler.
func sessionMiddleware(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
return
}
r = r.WithContext(context.WithValue(r.Context(), "session", session))
h(w, r)
}
}
Now your handlers will still need to "get" the session value from the context however any handler that is wrapped by sessionMiddleware can assume, when it's executed, that a session is present in the context and therefore it can skip the error checking.
func indexHandler(w http.ResponseWriter, req *http.Request) {
session := req.Context().Value("session").(*sessions.Session)
err := views.ExecuteTemplate(w, "index.html", session.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
And to register the handler you would do:
app.HandleFunc("/", sessionMiddleware(indexHandler))
If this is still too much code repetition for your taste you can pass the session directly to your handlers however you'll have to change their signature.
type SessionHandler func(w http.ResponseWriter, r *http.Request, s *session.Session)
Then update your handlers.
func indexHandler(w http.ResponseWriter, req *http.Request, s *session.Session) {
err := views.ExecuteTemplate(w, "index.html", s.Values)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
And you can define the middleware part on the SessionHandler
type as a method.
func (h SessionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, sessionName)
if err != nil {
log.WithError(err).Error("bad session")
http.SetCookie(w, &http.Cookie{Name: sessionName, MaxAge: -1, Path: "/"})
return
}
h(w, r, session)
}
And then to register the handler you would do:
app.Handle("/", SessionHandler(indexHandler))
Upvotes: 6