用于Go的会话中间件?

时间:2019-05-03 04:34:42

标签: session go gorilla

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

我所有的处理程序都使用updater function (check doc for more details)。如何避免在每个处理程序中store.Get设置会话,即代码重复?

另一个问题是,有没有一种更好的方式为模板提供会话值,而其他方式则是明确的,例如:

err = views.ExecuteTemplate(w, "index.html",
    struct {
        Session map[interface{}]interface{},
        ...other handler specific data for the template
    }{
        session.Values,
        ...
    })

代码示例源自Gorilla sessions

1 个答案:

答案 0 :(得分:2)

对于中间件,您可以定义一个将处理程序作为参数并返回另一个处理程序作为结果的函数。

func sessionMiddleware(h http.HandlerFunc) http.HandlerFunc {
    // ...
}

返回的处理程序可以store.Get进行会话,如果不存在则返回错误,如果存在则将错误存储在请求的上下文中,然后调用实际的处理程序。

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

现在,您的处理程序仍将需要从上下文中“获取”会话值,但是,由sessionMiddleware包装的任何处理程序都可以在执行时假设会话存在于上下文中,因此可以跳过错误检查

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

要注册处理程序,您将执行以下操作:

app.HandleFunc("/", sessionMiddleware(indexHandler))

如果您仍然觉得代码重复太多,可以将会话直接传递给处理程序,但是您必须更改其签名。

type SessionHandler func(w http.ResponseWriter, r *http.Request, s *session.Session)

然后更新您的处理程序。

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

您可以将SessionHandler类型的中间件部分定义为方法。

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

然后要注册处理程序:

app.Handle("/", SessionHandler(indexHandler))