session

session 中间件为 Flame 实例提供用户会话管理服务,支持的存储后端包括内存、文件系统、PostgreSQL、MySQL、Redis 和 MongoDB。

你可以在 GitHubsession - 图1在新窗口打开 上阅读该中间件的源码或通过 pkg.go.devsession - 图2在新窗口打开 查看 API 文档。

下载安装

Go 语言的最低版本要求为 1.16

  1. go get github.com/flamego/session

存储后端

注意

本小结仅展示 session 中间件的相关用法,示例中的用户验证方案绝不可以直接被用于生产环境。

内存

session.Sessionersession - 图3在新窗口打开 可以配合 session.Optionssession - 图4在新窗口打开 对中间件进行配置,并默认使用内存作为存储后端:

  1. package main
  2. import (
  3. "strconv"
  4. "github.com/flamego/flamego"
  5. "github.com/flamego/session"
  6. )
  7. func main() {
  8. f := flamego.Classic()
  9. f.Use(session.Sessioner())
  10. f.Get("/set", func(s session.Session) string {
  11. s.Set("user_id", 123)
  12. return "Succeed"
  13. })
  14. f.Get("/get", func(s session.Session) string {
  15. userID, ok := s.Get("user_id").(int)
  16. if !ok || userID <= 0 {
  17. return "Not authenticated"
  18. }
  19. return "Authenticated as " + strconv.Itoa(userID)
  20. })
  21. f.Get("/clear", func(s session.Session) string {
  22. s.Delete("user_id")
  23. return "Cleared"
  24. })
  25. f.Run()
  26. }

由于数据存储在内存中,因此会在应用退出后被清除。如需持久化会话数据,请选择其它存储后端。

文件系统

session.FileInitersession - 图5在新窗口打开 是文件系统存储后端的初始化函数,并可以配合 session.FileConfigsession - 图6在新窗口打开 对其进行配置:

  1. package main
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strconv"
  6. "github.com/flamego/flamego"
  7. "github.com/flamego/session"
  8. )
  9. func main() {
  10. f := flamego.Classic()
  11. f.Use(session.Sessioner(
  12. session.Options{
  13. Initer: session.FileIniter(),
  14. Config: session.FileConfig{
  15. RootDir: filepath.Join(os.TempDir(), "sessions"),
  16. },
  17. },
  18. ))
  19. f.Get("/set", func(s session.Session) string {
  20. s.Set("user_id", 123)
  21. return "Succeed"
  22. })
  23. f.Get("/get", func(s session.Session) string {
  24. userID, ok := s.Get("user_id").(int)
  25. if !ok || userID <= 0 {
  26. return "Not authenticated"
  27. }
  28. return "Authenticated as " + strconv.Itoa(userID)
  29. })
  30. f.Get("/clear", func(s session.Session) string {
  31. s.Delete("user_id")
  32. return "Cleared"
  33. })
  34. f.Run()
  35. }

PostgreSQL

postgres.Initersession - 图7在新窗口打开 是 PostgreSQL 存储后端的初始化函数,并可以配合 postgres.Configsession - 图8在新窗口打开 对其进行配置:

  1. package main
  2. import (
  3. "os"
  4. "strconv"
  5. "github.com/flamego/flamego"
  6. "github.com/flamego/session"
  7. "github.com/flamego/session/postgres"
  8. )
  9. func main() {
  10. f := flamego.Classic()
  11. dsn := os.ExpandEnv("postgres://$PGUSER:$PGPASSWORD@$PGHOST:$PGPORT/$PGDATABASE?sslmode=$PGSSLMODE")
  12. f.Use(session.Sessioner(
  13. session.Options{
  14. Initer: postgres.Initer(),
  15. Config: postgres.Config{
  16. DSN: dsn,
  17. Table: "sessions",
  18. InitTable: true,
  19. },
  20. },
  21. ))
  22. f.Get("/set", func(s session.Session) string {
  23. s.Set("user_id", 123)
  24. return "Succeed"
  25. })
  26. f.Get("/get", func(s session.Session) string {
  27. userID, ok := s.Get("user_id").(int)
  28. if !ok || userID <= 0 {
  29. return "Not authenticated"
  30. }
  31. return "Authenticated as " + strconv.Itoa(userID)
  32. })
  33. f.Get("/clear", func(s session.Session) string {
  34. s.Delete("user_id")
  35. return "Cleared"
  36. })
  37. f.Run()
  38. }

MySQL

mysql.Initersession - 图9在新窗口打开 是 MySQL 存储后端的初始化函数,并可以配合 mysql.Configsession - 图10在新窗口打开 对其进行配置:

  1. package main
  2. import (
  3. "os"
  4. "strconv"
  5. "github.com/flamego/flamego"
  6. "github.com/flamego/session"
  7. "github.com/flamego/session/mysql"
  8. )
  9. func main() {
  10. f := flamego.Classic()
  11. dsn := os.ExpandEnv("$MYSQL_USER:$MYSQL_PASSWORD@tcp($MYSQL_HOST:$MYSQL_PORT)/$MYSQL_DATABASE?charset=utf8&parseTime=true")
  12. f.Use(session.Sessioner(
  13. session.Options{
  14. Initer: mysql.Initer(),
  15. Config: mysql.Config{
  16. DSN: dsn,
  17. Table: "cache",
  18. InitTable: true,
  19. },
  20. },
  21. ))
  22. f.Get("/set", func(s session.Session) string {
  23. s.Set("user_id", 123)
  24. return "Succeed"
  25. })
  26. f.Get("/get", func(s session.Session) string {
  27. userID, ok := s.Get("user_id").(int)
  28. if !ok || userID <= 0 {
  29. return "Not authenticated"
  30. }
  31. return "Authenticated as " + strconv.Itoa(userID)
  32. })
  33. f.Get("/clear", func(s session.Session) string {
  34. s.Delete("user_id")
  35. return "Cleared"
  36. })
  37. f.Run()
  38. }

Redis

redis.Initersession - 图11在新窗口打开 是 Redis 存储后端的初始化函数,并可以配合 redis.Configsession - 图12在新窗口打开 对其进行配置:

  1. package main
  2. import (
  3. "os"
  4. "strconv"
  5. "github.com/flamego/flamego"
  6. "github.com/flamego/session"
  7. "github.com/flamego/session/redis"
  8. )
  9. func main() {
  10. f := flamego.Classic()
  11. f.Use(session.Sessioner(
  12. session.Options{
  13. Initer: redis.Initer(),
  14. Config: redis.Config{
  15. Options: &redis.Options{
  16. Addr: os.ExpandEnv("$REDIS_HOST:$REDIS_PORT"),
  17. DB: 15,
  18. },
  19. },
  20. },
  21. ))
  22. f.Get("/set", func(s session.Session) string {
  23. s.Set("user_id", 123)
  24. return "Succeed"
  25. })
  26. f.Get("/get", func(s session.Session) string {
  27. userID, ok := s.Get("user_id").(int)
  28. if !ok || userID <= 0 {
  29. return "Not authenticated"
  30. }
  31. return "Authenticated as " + strconv.Itoa(userID)
  32. })
  33. f.Get("/clear", func(s session.Session) string {
  34. s.Delete("user_id")
  35. return "Cleared"
  36. })
  37. f.Run()
  38. }

MongoDB

mongo.Initersession - 图13在新窗口打开 是 MongoDB 存储后端的初始化函数,并可以配合 mongo.Configsession - 图14在新窗口打开 对其进行配置:

  1. package main
  2. import (
  3. "os"
  4. "strconv"
  5. "github.com/flamego/flamego"
  6. "github.com/flamego/session"
  7. "github.com/flamego/session/mongo"
  8. "go.mongodb.org/mongo-driver/mongo/options"
  9. )
  10. func main() {
  11. f := flamego.Classic()
  12. f.Use(session.Sessioner(
  13. session.Options{
  14. Initer: mongo.Initer(),
  15. Config: mongo.Config{
  16. Options: options.Client().ApplyURI(os.Getenv("MONGODB_URI")),
  17. Database: os.Getenv("MONGODB_DATABASE"),
  18. Collection: "cache",
  19. },
  20. },
  21. ))
  22. f.Get("/set", func(s session.Session) string {
  23. s.Set("user_id", 123)
  24. return "Succeed"
  25. })
  26. f.Get("/get", func(s session.Session) string {
  27. userID, ok := s.Get("user_id").(int)
  28. if !ok || userID <= 0 {
  29. return "Not authenticated"
  30. }
  31. return "Authenticated as " + strconv.Itoa(userID)
  32. })
  33. f.Get("/clear", func(s session.Session) string {
  34. s.Delete("user_id")
  35. return "Cleared"
  36. })
  37. f.Run()
  38. }

闪现消息

session 中间件提供了闪现消息的机制,闪现消息指的是在下次会话展现给用户的消息,并且只会展现一次。

闪现消息最简单的形式就是字符串:

  1. package main
  2. import (
  3. "github.com/flamego/flamego"
  4. "github.com/flamego/session"
  5. )
  6. func main() {
  7. f := flamego.Classic()
  8. f.Use(session.Sessioner())
  9. f.Get("/set", func(s session.Session) string {
  10. s.SetFlash("This is a flash message")
  11. return "Succeed"
  12. })
  13. f.Get("/get", func(f session.Flash) string {
  14. s, ok := f.(string)
  15. if !ok || s == "" {
  16. return "No flash message"
  17. }
  18. return s
  19. })
  20. f.Run()
  21. }

session.Flashsession - 图15在新窗口打开 是单纯作为闪现消息的载体而存在,你可以使用任意类型作为闪现消息的具体表现形式,且不同路由之间的闪现消息类型也可完全不同:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/flamego/flamego"
  5. "github.com/flamego/session"
  6. )
  7. func main() {
  8. f := flamego.Classic()
  9. f.Use(session.Sessioner())
  10. f.Get("/set-simple", func(s session.Session) string {
  11. s.SetFlash("This is a flash message")
  12. return "Succeed"
  13. })
  14. f.Get("/get-simple", func(f session.Flash) string {
  15. s, ok := f.(string)
  16. if !ok || s == "" {
  17. return "No flash message"
  18. }
  19. return s
  20. })
  21. type Flash struct {
  22. Success string
  23. Error string
  24. }
  25. f.Get("/set-complex", func(s session.Session) string {
  26. s.SetFlash(Flash{
  27. Success: "It worked!",
  28. })
  29. return "Succeed"
  30. })
  31. f.Get("/get-complex", func(f session.Flash) string {
  32. s, ok := f.(Flash)
  33. if !ok {
  34. return "No flash message"
  35. }
  36. return fmt.Sprintf("%#v", s)
  37. })
  38. f.Run()
  39. }

上例展示了如何在不同的路由中使用不同类型的闪现消息(string and Flash)。