improve session storage interface for gsession

This commit is contained in:
john 2019-09-15 11:30:38 +08:00
parent 6d55b30b39
commit f128cb9f61
4 changed files with 156 additions and 50 deletions

View File

@ -40,13 +40,12 @@
## 功能改进 ## 功能改进
1. `ghttp` 1. `ghttp`
- 当`WebServer`产生`panic`异常错误时,默认打印调用链堆栈到错误日志中;
- `Cookie`及`Session`的`TTL`配置数据类型修改为`time.Duration`; - `Cookie`及`Session`的`TTL`配置数据类型修改为`time.Duration`;
- 新增允许同时通过`Header/Cookie`传递`SessionId` - 新增允许同时通过`Header/Cookie`传递`SessionId`
- 新增`ConfigFromMap/SetConfigWithMap`方法,支持通过`map`参数设置WebServer - 新增`ConfigFromMap/SetConfigWithMap`方法,支持通过`map`参数设置WebServer
- 改进默认的`CORS`配置,增加对常见`Header`参数的默认支持; - 改进默认的`CORS`配置,增加对常见`Header`参数的默认支持;
- 新增`IsExitError`方法,用于开发者自定义处理`recover`错误处理时,过滤框架本身自定义的非异常错误; - 新增`IsExitError`方法,用于开发者自定义处理`recover`错误处理时,过滤框架本身自定义的非异常错误;
- 新增`SetSessionStorage`方法,用于开发者自定义`Session`存储; - 新增`SetSessionStorage`配置方法,用于开发者自定义`Session`存储;
- `ghttp.Request`新增更多的参数获取方法; - `ghttp.Request`新增更多的参数获取方法;
1. `gdb` 1. `gdb`
- 增加对SQL中部分字段的自动转义(`Quote`)功能; - 增加对SQL中部分字段的自动转义(`Quote`)功能;
@ -54,6 +53,7 @@
- 增加`SetLogger`方法用于开发者自定义数据库的日志打印; - 增加`SetLogger`方法用于开发者自定义数据库的日志打印;
- 增加`Master/Slave`方法,开发者可自主选择数据库操作执行的主从节点; - 增加`Master/Slave`方法,开发者可自主选择数据库操作执行的主从节点;
- 增加对`mssql/pgsql/oracle`的单元测试; - 增加对`mssql/pgsql/oracle`的单元测试;
- `debug`模式支持完整带参数整合的SQL语句调试打印
- 增加了更多的功能方法; - 增加了更多的功能方法;
1. `glog` 1. `glog`
- 新增`Default`方法用于获取默认的`Logger`对象; - 新增`Default`方法用于获取默认的`Logger`对象;

View File

@ -32,7 +32,7 @@ func (s *Session) init() {
s.dirty = gtype.NewBool(false) s.dirty = gtype.NewBool(false)
} }
if len(s.id) > 0 && s.data == nil { if len(s.id) > 0 && s.data == nil {
if data := s.manager.storage.Get(s.id); data != nil { if data := s.manager.storage.GetSession(s.id); data != nil {
if s.data = gmap.NewStrAnyMapFrom(data, true); s.data == nil { if s.data = gmap.NewStrAnyMapFrom(data, true); s.data == nil {
panic("session restoring failed for id:" + s.id) panic("session restoring failed for id:" + s.id)
} }
@ -51,6 +51,73 @@ func (s *Session) init() {
} }
} }
// Set sets key-value pair to this session.
func (s *Session) Set(key string, value interface{}) error {
s.init()
if err := s.manager.storage.Set(key, value); err != nil {
if err == ErrorDisabled {
s.data.Set(key, value)
} else {
return err
}
}
s.dirty.Set(true)
return nil
}
// Sets batch sets the session using map.
func (s *Session) Sets(data map[string]interface{}) error {
s.init()
if err := s.manager.storage.SetMap(data); err != nil {
if err == ErrorDisabled {
s.data.Sets(data)
} else {
return err
}
}
s.dirty.Set(true)
return nil
}
// Remove removes key along with its value from this session.
func (s *Session) Remove(key string) error {
if len(s.id) == 0 {
return nil
}
s.init()
if err := s.manager.storage.Remove(key); err != nil {
if err == ErrorDisabled {
s.data.Remove(key)
} else {
return err
}
}
s.dirty.Set(true)
return nil
}
// Clear is alias of RemoveAll.
func (s *Session) Clear() error {
return s.RemoveAll()
}
// RemoveAll deletes all key-value pairs from this session.
func (s *Session) RemoveAll() error {
if len(s.id) == 0 {
return nil
}
s.init()
if err := s.manager.storage.RemoveAll(); err != nil {
if err == ErrorDisabled {
s.data.Clear()
} else {
return err
}
}
s.dirty.Set(true)
return nil
}
// Id returns the session id for this session. // Id returns the session id for this session.
// It create and returns a new session id if the session id is not passed in initialization. // It create and returns a new session id if the session id is not passed in initialization.
func (s *Session) Id() string { func (s *Session) Id() string {
@ -63,6 +130,9 @@ func (s *Session) Id() string {
func (s *Session) Map() map[string]interface{} { func (s *Session) Map() map[string]interface{} {
if len(s.id) > 0 { if len(s.id) > 0 {
s.init() s.init()
if data := s.manager.storage.GetMap(); data != nil {
return data
}
return s.data.Map() return s.data.Map()
} }
return nil return nil
@ -72,29 +142,18 @@ func (s *Session) Map() map[string]interface{} {
func (s *Session) Size() int { func (s *Session) Size() int {
if len(s.id) > 0 { if len(s.id) > 0 {
s.init() s.init()
if size := s.manager.storage.GetSize(s.id); size >= 0 {
return size
}
return s.data.Size() return s.data.Size()
} }
return 0 return 0
} }
// Set sets key-value pair to this session.
func (s *Session) Set(key string, value interface{}) {
s.init()
s.data.Set(key, value)
s.dirty.Set(true)
}
// Sets batch sets the session using map.
func (s *Session) Sets(m map[string]interface{}) {
s.init()
s.data.Sets(m)
s.dirty.Set(true)
}
// Contains checks whether key exist in the session. // Contains checks whether key exist in the session.
func (s *Session) Contains(key string) bool { func (s *Session) Contains(key string) bool {
s.init() s.init()
return s.data.Contains(key) return s.Get(key) != nil
} }
// IsDirty checks whether there's any data changes in the session. // IsDirty checks whether there's any data changes in the session.
@ -105,22 +164,6 @@ func (s *Session) IsDirty() bool {
return s.dirty.Val() return s.dirty.Val()
} }
// Remove removes key along with its value from this session.
func (s *Session) Remove(key string) {
s.init()
s.data.Remove(key)
s.dirty.Set(true)
}
// Clear deletes all key-value pairs from this session.
func (s *Session) Clear() {
if len(s.id) > 0 {
s.init()
s.data.Clear()
s.dirty.Set(true)
}
}
// Close closes current session and updates its ttl in the session manager. // Close closes current session and updates its ttl in the session manager.
// If this session is dirty, it also exports it to storage. // If this session is dirty, it also exports it to storage.
// //
@ -130,7 +173,7 @@ func (s *Session) Close() {
if s.manager.storage != nil { if s.manager.storage != nil {
if s.dirty.Cas(true, false) { if s.dirty.Cas(true, false) {
s.data.RLockFunc(func(m map[string]interface{}) { s.data.RLockFunc(func(m map[string]interface{}) {
if err := s.manager.storage.Set(s.id, m); err != nil { if err := s.manager.storage.SetSession(s.id, m); err != nil {
panic(err) panic(err)
} }
}) })
@ -144,12 +187,19 @@ func (s *Session) Close() {
} }
} }
// Get retrieves session value with given key.
// It returns <def> if the key does not exist in the session if <def> is given,
// or else it return nil.
func (s *Session) Get(key string, def ...interface{}) interface{} { func (s *Session) Get(key string, def ...interface{}) interface{} {
if len(s.id) > 0 { if len(s.id) == 0 {
return nil
}
s.init() s.init()
if v := s.data.Get(key); v != nil { if v := s.manager.storage.Get(key); v != nil {
return v return v
} }
if v := s.data.Get(key); v != nil {
return v
} }
if len(def) > 0 { if len(def) > 0 {
return def[0] return def[0]

View File

@ -7,11 +7,29 @@
package gsession package gsession
type Storage interface { type Storage interface {
// Get returns the session data bytes for given session id. // Get retrieves session value with given key.
Get(id string) map[string]interface{} // It returns nil if the key does not exist in the session.
// Set updates the content for session id. Get(key string) interface{}
// GetMap retrieves all key-value pairs as map from storage.
GetMap() map[string]interface{}
// GetSize retrieves the size of key-value pairs from storage.
GetSize(id string) int
// Set sets key-value session pair to the storage.
Set(key string, value interface{}) error
// SetMap batch sets key-value session pairs with map to the storage.
SetMap(data map[string]interface{}) error
// Remove deletes key with its value from storage.
Remove(key string) error
// RemoveAll deletes all key-value pairs from storage.
RemoveAll() error
// GetSession returns the session data bytes for given session id.
GetSession(id string) map[string]interface{}
// SetSession updates the content for session id.
// Note that the parameter <content> is the serialized bytes for session map. // Note that the parameter <content> is the serialized bytes for session map.
Set(id string, data map[string]interface{}) error SetSession(id string, data map[string]interface{}) error
// UpdateTTL updates the TTL for session id. // UpdateTTL updates the TTL for specified session id.
UpdateTTL(id string) error UpdateTTL(id string) error
} }

View File

@ -8,6 +8,7 @@ package gsession
import ( import (
"encoding/json" "encoding/json"
"errors"
"os" "os"
"time" "time"
@ -39,6 +40,7 @@ var (
DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!")
DefaultStorageFileCryptoEnabled = false DefaultStorageFileCryptoEnabled = false
DefaultStorageFileLoopInterval = time.Minute DefaultStorageFileLoopInterval = time.Minute
ErrorDisabled = errors.New("this feature is disabled in this storage")
) )
func init() { func init() {
@ -96,8 +98,44 @@ func (s *StorageFile) sessionFilePath(id string) string {
return gfile.Join(s.path, id) return gfile.Join(s.path, id)
} }
// Get return the session data for given session id. // Get retrieves session value with given key.
func (s *StorageFile) Get(id string) map[string]interface{} { // It returns nil if the key does not exist in the session.
func (s *StorageFile) Get(key string) interface{} {
return nil
}
// GetMap retrieves all key-value pairs as map from storage.
func (s *StorageFile) GetMap() map[string]interface{} {
return nil
}
// GetSize retrieves the size of key-value pairs from storage.
func (s *StorageFile) GetSize(id string) int {
return -1
}
// Set sets key-value session pair to the storage.
func (s *StorageFile) Set(key string, value interface{}) error {
return ErrorDisabled
}
// SetMap batch sets key-value session pairs with map to the storage.
func (s *StorageFile) SetMap(data map[string]interface{}) error {
return ErrorDisabled
}
// Remove deletes key with its value from storage.
func (s *StorageFile) Remove(key string) error {
return ErrorDisabled
}
// RemoveAll deletes all key-value pairs from storage.
func (s *StorageFile) RemoveAll() error {
return ErrorDisabled
}
// GetSession return the session data for given session id.
func (s *StorageFile) GetSession(id string) map[string]interface{} {
path := s.sessionFilePath(id) path := s.sessionFilePath(id)
data := gfile.GetBytes(path) data := gfile.GetBytes(path)
if len(data) > 8 { if len(data) > 8 {
@ -123,9 +161,9 @@ func (s *StorageFile) Get(id string) map[string]interface{} {
return nil return nil
} }
// Set updates the content for session id. // SetSession updates the content for session id.
// Note that the parameter <content> is the serialized bytes for session map. // Note that the parameter <content> is the serialized bytes for session map.
func (s *StorageFile) Set(id string, data map[string]interface{}) error { func (s *StorageFile) SetSession(id string, data map[string]interface{}) error {
path := s.sessionFilePath(id) path := s.sessionFilePath(id)
content, err := json.Marshal(data) content, err := json.Marshal(data)
if err != nil { if err != nil {
@ -151,7 +189,7 @@ func (s *StorageFile) Set(id string, data map[string]interface{}) error {
return file.Close() return file.Close()
} }
// UpdateTTL updates the TTL for session id. // UpdateTTL updates the TTL for specified session id.
// It just adds the session id to the async handling queue. // It just adds the session id to the async handling queue.
func (s *StorageFile) UpdateTTL(id string) error { func (s *StorageFile) UpdateTTL(id string) error {
s.updatingIdSet.Add(id) s.updatingIdSet.Add(id)