From a88d609901a41f09973d2780d84f9d6736f9233b Mon Sep 17 00:00:00 2001 From: John Date: Sun, 31 Dec 2017 12:15:04 +0800 Subject: [PATCH] =?UTF-8?q?cookie,session=E8=A7=A3=E8=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/frame/gmvc/controller.go | 9 +- g/frame/gmvc/view.go | 21 ++-- g/net/ghttp/http_client.go | 8 -- g/net/ghttp/http_client_request.go | 14 ++- g/net/ghttp/http_server_cookie.go | 21 ++-- g/net/ghttp/http_server_handler.go | 22 ++--- .../http_server_session.go} | 99 +++++++------------ geg/frame/mvc/controller/demo/cookie.go | 19 ++++ geg/frame/mvc/controller/demo/hello.go | 16 +-- geg/frame/mvc/controller/demo/session.go | 19 ++++ 10 files changed, 125 insertions(+), 123 deletions(-) rename g/net/{gsession/gsession.go => ghttp/http_server_session.go} (55%) create mode 100644 geg/frame/mvc/controller/demo/cookie.go create mode 100644 geg/frame/mvc/controller/demo/session.go diff --git a/g/frame/gmvc/controller.go b/g/frame/gmvc/controller.go index c9aeb84bb..95ff3335a 100644 --- a/g/frame/gmvc/controller.go +++ b/g/frame/gmvc/controller.go @@ -9,7 +9,6 @@ package gmvc import ( "gitee.com/johng/gf/g/net/ghttp" - "gitee.com/johng/gf/g/net/gsession" ) // 控制器基类 @@ -18,7 +17,7 @@ type Controller struct { Request *ghttp.ClientRequest // 请求数据对象 Response *ghttp.ServerResponse // 返回数据对象 Cookie *ghttp.Cookie // COOKIE操作对象 - Session *gsession.Session // SESSION操作对象 + Session *ghttp.Session // SESSION操作对象 View *View // 视图对象 } @@ -27,9 +26,9 @@ func (c *Controller) Init(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.Serv c.Server = s c.Request = r c.Response = w - c.Cookie = ghttp.NewCookie(c.Request, c.Response) - c.View = NewView(c) - c.Session = gsession.Get(c.Cookie.SessionId()) + c.View = NewView(w) + c.Cookie = r.Cookie + c.Session = r.Session } // 控制器结束请求接口方法 diff --git a/g/frame/gmvc/view.go b/g/frame/gmvc/view.go index bdb449f74..b65ca1a3d 100644 --- a/g/frame/gmvc/view.go +++ b/g/frame/gmvc/view.go @@ -11,22 +11,23 @@ import ( "html/template" "gitee.com/johng/gf/g/os/gview" "gitee.com/johng/gf/g/frame/gins" + "gitee.com/johng/gf/g/net/ghttp" ) // 视图对象(一个请求一个视图对象,用完即销毁) type View struct { - mu sync.RWMutex // 并发互斥锁 - ctl *Controller // 所属控制器 - view *gview.View // 底层视图对象 - data map[string]interface{} // 视图数据 + mu sync.RWMutex // 并发互斥锁 + view *gview.View // 底层视图对象 + data map[string]interface{} // 视图数据 + response *ghttp.ServerResponse // 数据返回对象 } // 创建一个MVC请求中使用的视图对象 -func NewView(c *Controller) *View { +func NewView(w *ghttp.ServerResponse) *View { return &View{ - ctl : c, - view : gins.View(), - data : make(map[string]interface{}), + view : gins.View(), + data : make(map[string]interface{}), + response : w, } } @@ -69,10 +70,10 @@ func (view *View) Display(files...string) error { file = files[0] } if content, err := view.Parse(file); err != nil { - view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error()) + view.response.WriteString("Tpl Parsing Error: " + err.Error()) return err } else { - view.ctl.Response.Write(content) + view.response.Write(content) } return nil } diff --git a/g/net/ghttp/http_client.go b/g/net/ghttp/http_client.go index 3051995a2..c321159c3 100644 --- a/g/net/ghttp/http_client.go +++ b/g/net/ghttp/http_client.go @@ -9,7 +9,6 @@ import ( "net/http" "strings" "time" - "net/url" "bytes" ) @@ -18,13 +17,6 @@ type Client struct { http.Client } -// 请求对象 -type ClientRequest struct { - http.Request - id uint64 // 请求id(唯一) - getvals *url.Values // GET参数 -} - // 客户端请求结果对象 type ClientResponse struct { http.Response diff --git a/g/net/ghttp/http_client_request.go b/g/net/ghttp/http_client_request.go index 132b6cf33..69bd45203 100644 --- a/g/net/ghttp/http_client_request.go +++ b/g/net/ghttp/http_client_request.go @@ -7,13 +7,19 @@ package ghttp import ( "io/ioutil" - "gitee.com/johng/gf/g/encoding/gjson" + "net/http" + "net/url" "gitee.com/johng/gf/g/util/gconv" + "gitee.com/johng/gf/g/encoding/gjson" ) -// 获取当前请求的id -func (r *ClientRequest) Id() uint64 { - return r.id +// 请求对象 +type ClientRequest struct { + http.Request + getvals *url.Values // GET参数 + Id uint64 // 请求id(唯一) + Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) + Session *Session // 与当前请求绑定的Session对象(并发安全) } // 获得指定名称的get参数列表 diff --git a/g/net/ghttp/http_server_cookie.go b/g/net/ghttp/http_server_cookie.go index 92f1265ce..14c69e143 100644 --- a/g/net/ghttp/http_server_cookie.go +++ b/g/net/ghttp/http_server_cookie.go @@ -45,7 +45,7 @@ var cookies = gmap.NewUintInterfaceMap() // 创建一个cookie对象,与传入的请求对应 func NewCookie(r *ClientRequest, w *ServerResponse) *Cookie { - if r := GetCookie(r.Id()); r != nil { + if r := GetCookie(r.Id); r != nil { return r } c := &Cookie { @@ -55,7 +55,7 @@ func NewCookie(r *ClientRequest, w *ServerResponse) *Cookie { response : w, } c.init() - cookies.Set(uint(r.Id()), c) + cookies.Set(uint(r.Id), c) return c } @@ -67,11 +67,6 @@ func GetCookie(requestid uint64) *Cookie { return nil } -// 请求完毕后删除已经存在的Cookie对象 -func RemoveCookie(requestid uint64) { - cookies.Remove(uint(requestid)) -} - // 获取默认的domain参数 func defaultDomain(r *ClientRequest) string { return strings.Split(r.Host, ":")[0] @@ -90,7 +85,12 @@ func (c *Cookie) init() { // 获取SessionId func (c *Cookie) SessionId() string { - return c.Get(SESSION_ID_NAME) + v := c.Get(SESSION_ID_NAME) + if v == "" { + v = makeSessionId() + c.SetSessionId(v) + } + return v } // 设置SessionId @@ -132,6 +132,11 @@ func (c *Cookie) Remove(key, domain, path string) { c.SetCookie(key, "", domain, path, -86400) } +// 请求完毕后删除已经存在的Cookie对象 +func (c *Cookie) Close() { + cookies.Remove(uint(c.request.Id)) +} + // 输出到客户端 func (c *Cookie) Output() { c.mu.RLock() diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index fe02a6dc7..fca4f7e9f 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -16,7 +16,6 @@ import ( "net/http" "path/filepath" "gitee.com/johng/gf/g/os/gfile" - "gitee.com/johng/gf/g/net/gsession" "gitee.com/johng/gf/g/encoding/ghtml" ) @@ -39,7 +38,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { // 构造请求/返回参数对象 request := &ClientRequest{} response := &ServerResponse{} - request.id = s.increServed() + request.Id = s.increServed() request.Request = *r response.ResponseWriter = w if h := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); h != nil { @@ -55,13 +54,10 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { // 初始化控制器 func (s *Server)callHandler(h *HandlerItem, r *ClientRequest, w *ServerResponse) { - // 会话处理,每个请求必定有一个sessionid - cookie := NewCookie(r, w) - sessionid := cookie.SessionId() - if sessionid == "" { - sessionid = gsession.Id() - cookie.SetSessionId(sessionid) - } + // 会话处理 + r.Cookie = NewCookie(r, w) + r.Session = GetSession(r.Cookie.SessionId()) + // 请求处理 if h.faddr == nil { // 新建一个控制器对象处理请求 @@ -80,13 +76,15 @@ func (s *Server)callHandler(h *HandlerItem, r *ClientRequest, w *ServerResponse) } // 输出Cookie - cookie.Output() + r.Cookie.Output() // 输出缓冲区 w.OutputBuffer() - // 删除当前会话的Cookie - RemoveCookie(r.Id()) + // 关闭当前会话的Cookie + go r.Cookie.Close() + // 更新Sssion会话超时时间 + go r.Session.UpdateExpire() } // 处理静态文件请求 diff --git a/g/net/gsession/gsession.go b/g/net/ghttp/http_server_session.go similarity index 55% rename from g/net/gsession/gsession.go rename to g/net/ghttp/http_server_session.go index 11281c522..44f0867a3 100644 --- a/g/net/gsession/gsession.go +++ b/g/net/ghttp/http_server_session.go @@ -3,8 +3,8 @@ // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://gitee.com/johng/gf. - -package gsession +// 并发安全的Session管理器 +package ghttp import ( "sync" @@ -14,10 +14,8 @@ import ( "gitee.com/johng/gf/g/os/gcache" "gitee.com/johng/gf/g/util/grand" "gitee.com/johng/gf/g/container/gmap" -) - -const ( - DEFAULT_EXPIRE_TIME = 600 // 默认过期间隔(10分钟) + "gitee.com/johng/gf/g/util/gconv" + "sync/atomic" ) // 单个session对象 @@ -25,114 +23,93 @@ type Session struct { mu sync.RWMutex // 并发安全互斥锁 id string // sessionid data *gmap.StringInterfaceMap // session数据 - expire int // 过期间隔(秒) } +// 默认session过期时间(秒) +var defaultSessionMaxAge int32 = 600 + // 生成一个唯一的sessionid字符串 -func Id() string { +func makeSessionId() string { return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 32) + grand.RandStr(3)) } +// 设置默认的session过期时间 +func SetSessionMaxAge(maxage int) { + atomic.StoreInt32(&defaultSessionMaxAge, int32(maxage)) +} + // 获取或者生成一个session对象 -func Get(sessionid string) *Session { - if r := gcache.Get(cacheKey(sessionid)); r != nil { +func GetSession(sessionid string) *Session { + if r := gcache.Get(sessionCacheKey(sessionid)); r != nil { return r.(*Session) } s := &Session { id : sessionid, data : gmap.NewStringInterfaceMap(), - expire : DEFAULT_EXPIRE_TIME, } - s.updateExpire() return s } // session在gache中的缓存键名 -func cacheKey(sessionid string) string { +func sessionCacheKey(sessionid string) string { return "session_" + sessionid } // 获取sessionid -func (s *Session) Id () string { - go s.updateExpire() +func (s *Session) Id() string { return s.id } // 获取当前session所有数据 func (s *Session) Data () map[string]interface{} { - go s.updateExpire() return *s.data.Clone() } -// 设置session过期间隔(秒) -func (s *Session) SetExpire (expire int) { - s.mu.Lock() - defer s.mu.Unlock() - go s.updateExpire() - s.expire = expire -} - // 设置session func (s *Session) Set (k string, v interface{}) { - go s.updateExpire() s.data.Set(k, v) } +// 批量设置 +func (s *Session) BatchSet (m map[string]interface{}) { + s.data.BatchSet(m) +} + // 获取session func (s *Session) Get (k string) interface{} { - go s.updateExpire() return s.data.Get(k) } +func (s *Session) GetString (k string) string { + return gconv.String(s.Get(k)) +} + +func (s *Session) GetBool (k string) bool { + return gconv.Bool(s.Get(k)) +} + func (s *Session) GetInt (k string) int { - go s.updateExpire() - if r := s.data.Get(k); r != nil { - return r.(int) - } - return 0 + return gconv.Int(s.Get(k)) } func (s *Session) GetUint (k string) uint { - go s.updateExpire() - if r := s.data.Get(k); r != nil { - return r.(uint) - } - return 0 + return gconv.Uint(s.Get(k)) } func (s *Session) GetFloat32 (k string) float32 { - go s.updateExpire() - if r := s.data.Get(k); r != nil { - return r.(float32) - } - return 0 + return gconv.Float32(s.Get(k)) } func (s *Session) GetFloat64 (k string) float64 { - go s.updateExpire() - if r := s.data.Get(k); r != nil { - return r.(float64) - } - return 0 -} - -// 获取session(字符串) -func (s *Session) GetString (k string) string { - go s.updateExpire() - if r := s.data.Get(k); r != nil { - return r.(string) - } - return "" + return gconv.Float64(s.Get(k)) } // 删除session func (s *Session) Remove (k string) { - go s.updateExpire() s.data.Remove(k) } -// 更新过期时间 -func (s *Session) updateExpire() { - //gcache.Set(cacheKey(s.id), s, int64(s.expire*1000)) - gcache.Set(cacheKey(s.id), s, 0) -} +// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) +func (s *Session) UpdateExpire() { + gcache.Set(sessionCacheKey(s.id), s, int64(defaultSessionMaxAge*1000)) +} \ No newline at end of file diff --git a/geg/frame/mvc/controller/demo/cookie.go b/geg/frame/mvc/controller/demo/cookie.go new file mode 100644 index 000000000..fa195e190 --- /dev/null +++ b/geg/frame/mvc/controller/demo/cookie.go @@ -0,0 +1,19 @@ +package demo + +import ( + "gitee.com/johng/gf/g/os/gtime" + "gitee.com/johng/gf/g/net/ghttp" +) + + +func init() { + ghttp.GetServer().BindHandler("/cookie", Cookie) +} + +// 用于函数映射 +func Cookie(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) { + datetime := r.Cookie.Get("datetime") + r.Cookie.Set("datetime", gtime.Datetime()) + + w.WriteString("datetime:" + datetime) +} \ No newline at end of file diff --git a/geg/frame/mvc/controller/demo/hello.go b/geg/frame/mvc/controller/demo/hello.go index c2c1f923c..32848b486 100644 --- a/geg/frame/mvc/controller/demo/hello.go +++ b/geg/frame/mvc/controller/demo/hello.go @@ -2,8 +2,6 @@ package demo import ( "gitee.com/johng/gf/g/net/ghttp" - "gitee.com/johng/gf/g/net/gsession" - "strconv" ) @@ -15,17 +13,5 @@ func init() { // 用于函数映射 func Hello(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) { - cookie := ghttp.GetCookie(r.Id()) - session := gsession.Get(cookie.SessionId()) - - id := 0 - for i := 0; i < 1; i++ { - if r := session.Get("id"); r != nil { - id = r.(int) - } - id++ - session.Set("id", id) - } - - w.WriteString("Hello World!" + strconv.Itoa(id)) + w.WriteString("Hello World!") } \ No newline at end of file diff --git a/geg/frame/mvc/controller/demo/session.go b/geg/frame/mvc/controller/demo/session.go new file mode 100644 index 000000000..0369df6d5 --- /dev/null +++ b/geg/frame/mvc/controller/demo/session.go @@ -0,0 +1,19 @@ +package demo + +import ( + "gitee.com/johng/gf/g/net/ghttp" + "strconv" +) + + +func init() { + ghttp.GetServer().BindHandler("/session", Session) +} + +// 用于函数映射 +func Session(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) { + id := r.Session.GetInt("id") + r.Session.Set("id", id + 1) + + w.WriteString("id:" + strconv.Itoa(id)) +} \ No newline at end of file