使用gorilla/sessions库时的最佳实践方法是什么?

huangapple go评论86阅读模式
英文:

Best practice with sessions (gorilla/sessions)

问题

在开始使用golang中的会话之前,我需要回答一些问题:

会话示例

import "github.com/gorilla/sessions"

var store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))

func Handler(w http.ResponseWriter, r *http.Request){
    session, _ := store.Get(r, "session-name")
    
    session.Values["foo"] = "bar"
    session.Values[42] = 43
    session.Save(r, w)
    
    fmt.Fprint(w, "Hello world :)")
}

func main(){
    store.Options = &sessions.Options{
        Domain:     "localhost",
        Path:       "/",
        MaxAge:     60 * 15,
        Secure:     false,
        HttpOnly:   true,
    }
}

Q1:

是否可以在同一域上添加具有不同名称的多个会话?

session1, _ := store.Get(r, "session-name-1")
session2, _ := store.Get(r, "session-name-2")

什么时候需要在同一域上使用多个会话?

Q2:

从会话中获取变量的最佳实践是什么?
my_session_var = session.Values["foo"]

Q3:

如何检查会话是否正确保存?如果同时访问相同的map来设置和获取变量?

更新

package main

import (
    "github.com/gorilla/sessions"
)

var (
    store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))
)

type handler func(w http.ResponseWriter, r *http.Request, s *sessions.Session)

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request){
    session, _ := store.Get(r, "session-name")
    
    h(w, r, session)
}

func Handler_404(w http.ResponseWriter, r *http.Request, s *sessions.Session){
    fmt.Fprint(w, "Oops, something went wrong!")
}

错误信息

# command-line-arguments
.\mux.go:101: 无法将Handler_404(类型为func(http.ResponseWriter, *http.Request, *sessions.Session))转换为类型http.HandlerFunc
英文:

Before starting using sessions in golang I need answers to some questions

session example

import "github.com/gorilla/sessions"

var store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))

func Handler(w http.ResponseWriter, r *http.Request){
    session, _ := store.Get(r, "session-name")
    
    session.Values["foo"] = "bar"
    session.Values[42] = 43
    session.Save(r, w)
    
    fmt.Fprint(w, "Hello world :)")
}

func main(){
    store.Options = &sessions.Options{
        Domain:     "localhost",
        Path:       "/",
        MaxAge:     60 * 15,
        Secure:     false,
        HttpOnly:   true,
    }
}

Q1:

Is it possible to add multiple sessions on the same domain with different names?

session1, _ := store.Get(r, "session-name-1")
session2, _ := store.Get(r, "session-name-2")

When do you need multiple sessions on the same domain?

Q2:

What is the best practice to get the variables from the session?
my_session_var = session.Values["foo"]

Q3:

How to check if the session is saved correctly? If you access the same map to both set and get variables?

update

package main

import (
    "github.com/gorilla/sessions"
)

var (
    store = sessions.NewCookieStore([]byte("33446a9dcf9ea060a0a6532b166da32f304af0de"))
)

type handler func(w http.ResponseWriter, r *http.Request, s *sessions.Session)

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request){
    session, _ := store.Get(r, "session-name")
    
    h(w, r, session)
}

func Handler_404(w http.ResponseWriter, r *http.Request, s *sessions.Session){
    fmt.Fprint(w, "Oops, something went wrong!")
}

error

# command-line-arguments
.\mux.go:101: cannot convert Handler_404 (type func(http.ResponseWriter, *http.Request, *sessions.Session)) to type http.HandlerFunc

答案1

得分: 5

《GO的HTTP处理程序的基本扩展》(Simon Whitehead)一文展示了在何时何地定义会话的示例。不必在Handler本身中定义会话,并且在定义其他处理程序时不必重复大量代码。

通过使用命名类型,您可以定义所需的Handler

type handler func(w http.ResponseWriter, r *http.Request, db *mgo.Database)

(在您的情况下,它将是gorilla会话,而不是mgo会话或数据库)

init()函数可以负责会话创建(这里是mgo会话,但对于其他框架会话,思路是相同的):

func init() {
    session, err = mgo.Dial("localhost")

    if err != nil {
        log.Println(err)
    }
}

您可以确保此函数类型(handler)遵守ServeHTTP()函数,负责:

  • 会话管理(克隆/关闭)
  • 调用实际的处理程序(可以有比wr更多的参数)
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s := session.Clone()
    defer s.Close()

    h(w, r, s.DB("example"))
}

然后,您可以定义实际的Handler(同样,具有比wr更多的参数):

func myHandler(w http.ResponseWriter, r *http.Request, db *mgo.Database) {
    var users []user

    db.C("users").Find(nil).All(&users)

    for _, user := range users {
        fmt.Fprintf(w, "%s is %d years old", user.Name, user.Age)
    }
}

并在服务器中使用该处理程序:

func main() {
    mux := http.NewServeMux()
    mux.Handle("/", handler(myHandler))
    http.ListenAndServe(":8080", mux)
}

这个想法是将main()中的“管道”最小化,同时拥有具有更多参数的Handler(包括会话)。这样可以使用不同的Handler,而只需很少的管道,使main()仅用于声明不同路径(而不是用于初始化会话和处理程序)。


2019年更新:在另一个相关的上下文中,还可以参考“如何处理会话”。

英文:

The article "BASIC EXTENSION OF GO’S HTTP HANDLERS" (Simon Whitehead) shows an example of where and when to define session.
Instead of doing it in the Handler itself, and having to duplicate a lot of code when you define other Handlers.

With a named type, you can define the Handler you need:

type handler func(w http.ResponseWriter, r *http.Request, db *mgo.Database)

(in your case, it would be a gorilla sessions instead of a mgo session or database)

The init() function can take care of the session creation (here mgo session, but the idea is the same for other framework sessions)

func init() {
	session, err = mgo.Dial("localhost")

	if err != nil {
		log.Println(err)
	}
}

And you can make sure this function type ('handler') does respect the ServeHTTP() function, taking care of:

  • the session management (clone/close)

  • calling your actual handler (which can have more parameters than just w and r)

      func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      	s := session.Clone()
          defer s.Close()
    
      	h(w, r, s.DB("example"))
      }
    

Then you can define your actual Handler (again, with more than w and r):

func myHandler(w http.ResponseWriter, r *http.Request, db *mgo.Database) {
	var users []user

	db.C("users").Find(nil).All(&users)

	for _, user := range users {
		fmt.Fprintf(w, "%s is %d years old", user.Name, user.Age)
	}
}

And you can use that handler in your server:

func main() {
	mux := http.NewServeMux()
	mux.Handle("/", handler(myHandler))
	http.ListenAndServe(":8080", mux)
}

The idea is to limit the "plumbing" in main() to a minimum, while having an Handler with more parameters (including your session).
That allows you to use different Handlers with very little plumbing, keeping main() only for the declaration of the different path (and not for the initialization of session and handlers)


Update 2019: in another related context, see also "How to handle sessions".

huangapple
  • 本文由 发表于 2014年9月30日 02:23:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/26106363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定