From f128cb9f611ed9859cf866b43aa2b82afd8983a2 Mon Sep 17 00:00:00 2001 From: john Date: Sun, 15 Sep 2019 11:30:38 +0800 Subject: [PATCH] improve session storage interface for gsession --- RELEASE.2.MD | 4 +- os/gsession/gsession_session.go | 126 +++++++++++++++++++-------- os/gsession/gsession_storage.go | 28 ++++-- os/gsession/gsession_storage_file.go | 48 ++++++++-- 4 files changed, 156 insertions(+), 50 deletions(-) diff --git a/RELEASE.2.MD b/RELEASE.2.MD index 3eb7d8c76..113aba9dd 100644 --- a/RELEASE.2.MD +++ b/RELEASE.2.MD @@ -40,13 +40,12 @@ ## 功能改进 1. `ghttp` - - 当`WebServer`产生`panic`异常错误时,默认打印调用链堆栈到错误日志中; - `Cookie`及`Session`的`TTL`配置数据类型修改为`time.Duration`; - 新增允许同时通过`Header/Cookie`传递`SessionId`; - 新增`ConfigFromMap/SetConfigWithMap`方法,支持通过`map`参数设置WebServer; - 改进默认的`CORS`配置,增加对常见`Header`参数的默认支持; - 新增`IsExitError`方法,用于开发者自定义处理`recover`错误处理时,过滤框架本身自定义的非异常错误; - - 新增`SetSessionStorage`方法,用于开发者自定义`Session`存储; + - 新增`SetSessionStorage`配置方法,用于开发者自定义`Session`存储; - `ghttp.Request`新增更多的参数获取方法; 1. `gdb` - 增加对SQL中部分字段的自动转义(`Quote`)功能; @@ -54,6 +53,7 @@ - 增加`SetLogger`方法用于开发者自定义数据库的日志打印; - 增加`Master/Slave`方法,开发者可自主选择数据库操作执行的主从节点; - 增加对`mssql/pgsql/oracle`的单元测试; + - `debug`模式支持完整带参数整合的SQL语句调试打印; - 增加了更多的功能方法; 1. `glog` - 新增`Default`方法用于获取默认的`Logger`对象; diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go index a6ad61248..1e8602a28 100644 --- a/os/gsession/gsession_session.go +++ b/os/gsession/gsession_session.go @@ -32,7 +32,7 @@ func (s *Session) init() { s.dirty = gtype.NewBool(false) } 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 { 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. // It create and returns a new session id if the session id is not passed in initialization. func (s *Session) Id() string { @@ -63,6 +130,9 @@ func (s *Session) Id() string { func (s *Session) Map() map[string]interface{} { if len(s.id) > 0 { s.init() + if data := s.manager.storage.GetMap(); data != nil { + return data + } return s.data.Map() } return nil @@ -72,29 +142,18 @@ func (s *Session) Map() map[string]interface{} { func (s *Session) Size() int { if len(s.id) > 0 { s.init() + if size := s.manager.storage.GetSize(s.id); size >= 0 { + return size + } return s.data.Size() } 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. func (s *Session) Contains(key string) bool { s.init() - return s.data.Contains(key) + return s.Get(key) != nil } // IsDirty checks whether there's any data changes in the session. @@ -105,22 +164,6 @@ func (s *Session) IsDirty() bool { 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. // 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.dirty.Cas(true, false) { 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) } }) @@ -144,12 +187,19 @@ func (s *Session) Close() { } } +// Get retrieves session value with given key. +// It returns if the key does not exist in the session if is given, +// or else it return nil. func (s *Session) Get(key string, def ...interface{}) interface{} { - if len(s.id) > 0 { - s.init() - if v := s.data.Get(key); v != nil { - return v - } + if len(s.id) == 0 { + return nil + } + s.init() + if v := s.manager.storage.Get(key); v != nil { + return v + } + if v := s.data.Get(key); v != nil { + return v } if len(def) > 0 { return def[0] diff --git a/os/gsession/gsession_storage.go b/os/gsession/gsession_storage.go index 12a0b8ea5..feb963f31 100644 --- a/os/gsession/gsession_storage.go +++ b/os/gsession/gsession_storage.go @@ -7,11 +7,29 @@ package gsession type Storage interface { - // Get returns the session data bytes for given session id. - Get(id string) map[string]interface{} - // Set updates the content for session id. + // Get retrieves session value with given key. + // It returns nil if the key does not exist in the session. + 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 is the serialized bytes for session map. - Set(id string, data map[string]interface{}) error - // UpdateTTL updates the TTL for session id. + SetSession(id string, data map[string]interface{}) error + // UpdateTTL updates the TTL for specified session id. UpdateTTL(id string) error } diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go index 3ecf4c582..cfcb75054 100644 --- a/os/gsession/gsession_storage_file.go +++ b/os/gsession/gsession_storage_file.go @@ -8,6 +8,7 @@ package gsession import ( "encoding/json" + "errors" "os" "time" @@ -39,6 +40,7 @@ var ( DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") DefaultStorageFileCryptoEnabled = false DefaultStorageFileLoopInterval = time.Minute + ErrorDisabled = errors.New("this feature is disabled in this storage") ) func init() { @@ -96,8 +98,44 @@ func (s *StorageFile) sessionFilePath(id string) string { return gfile.Join(s.path, id) } -// Get return the session data for given session id. -func (s *StorageFile) Get(id string) map[string]interface{} { +// Get retrieves session value with given key. +// 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) data := gfile.GetBytes(path) if len(data) > 8 { @@ -123,9 +161,9 @@ func (s *StorageFile) Get(id string) map[string]interface{} { return nil } -// Set updates the content for session id. +// SetSession updates the content for session id. // Note that the parameter 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) content, err := json.Marshal(data) if err != nil { @@ -151,7 +189,7 @@ func (s *StorageFile) Set(id string, data map[string]interface{}) error { 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. func (s *StorageFile) UpdateTTL(id string) error { s.updatingIdSet.Add(id)