diff --git a/g/frame/gmvc/controller.go b/g/frame/gmvc/controller.go index 4ccf347d6..bc7759371 100644 --- a/g/frame/gmvc/controller.go +++ b/g/frame/gmvc/controller.go @@ -4,5 +4,5 @@ import "gitee.com/johng/gf/g/net/ghttp" // 控制器基类 type Controller struct { - ghttp.Controller + ghttp.ControllerBase } diff --git a/g/frame/gmvc/controller_rest.go b/g/frame/gmvc/controller_rest.go deleted file mode 100644 index 9b640ad41..000000000 --- a/g/frame/gmvc/controller_rest.go +++ /dev/null @@ -1,8 +0,0 @@ -package gmvc - -import "gitee.com/johng/gf/g/net/ghttp" - -// 基于RESTful格式的控制器 -type Controller_Rest struct { - ghttp.ControllerRest -} diff --git a/g/net/ghttp/http_controller.go b/g/net/ghttp/http_controller.go index 8a6c03192..5e3b8cd61 100644 --- a/g/net/ghttp/http_controller.go +++ b/g/net/ghttp/http_controller.go @@ -1,39 +1,8 @@ package ghttp -import "gitee.com/johng/gf/g/net/gsession" - -// 控制器基类 -type Controller struct { +// 控制器接口 +type Controller interface { + Init() + Shut() } - -func (c *Controller) Get(*ClientRequest, *ServerResponse) {} -func (c *Controller) Put(*ClientRequest, *ServerResponse) {} -func (c *Controller) Post(*ClientRequest, *ServerResponse) {} -func (c *Controller) Delete(*ClientRequest, *ServerResponse) {} -func (c *Controller) Head(*ClientRequest, *ServerResponse) {} -func (c *Controller) Patch(*ClientRequest, *ServerResponse) {} -func (c *Controller) Connect(*ClientRequest, *ServerResponse) {} -func (c *Controller) Options(*ClientRequest, *ServerResponse) {} -func (c *Controller) Trace(*ClientRequest, *ServerResponse) {} - -// 获取当前请求的session对象 -func (c *Controller) Session(r *ClientRequest, w *ServerResponse) *gsession.Session { - sessionid := "" - if r, err := r.Cookie("gfsessionid"); err == nil { - sessionid = r.Value - } else { - sessionid = gsession.Id() - } - return gsession.Get(sessionid) -} - -// 请求初始化时的回调函数 -func (c *Controller) __init(r *ClientRequest, w *ServerResponse) { - -} - -// 请求结束时的回调函数 -func (c *Controller) __shut(r *ClientRequest, w *ServerResponse) { - -} \ No newline at end of file diff --git a/g/net/ghttp/http_controller_base.go b/g/net/ghttp/http_controller_base.go new file mode 100644 index 000000000..9c0bc7cef --- /dev/null +++ b/g/net/ghttp/http_controller_base.go @@ -0,0 +1,30 @@ +package ghttp + +import ( + "gitee.com/johng/gf/g/net/gsession" +) + +// 控制器基类 +type ControllerBase struct { + Request *ClientRequest + Response *ServerResponse + Cookie *Cookie + Session *gsession.Session +} + +// 控制器初始化 +func (c *ControllerBase) Init() { + c.Cookie = NewCookie(c.Request, c.Response) + if r := c.Cookie.Get("gfsessionid"); r != "" { + c.Session = gsession.Get(r) + } else { + c.Session = gsession.Get(gsession.Id()) + } +} + +// 控制器结束请求 +func (c *ControllerBase) Shut() { + c.Cookie.Output() +} + + diff --git a/g/net/ghttp/http_controller_rest.go b/g/net/ghttp/http_controller_rest.go deleted file mode 100644 index 183ab6a17..000000000 --- a/g/net/ghttp/http_controller_rest.go +++ /dev/null @@ -1,14 +0,0 @@ -package ghttp - -// RESTful控制器接口 -type ControllerRest interface { - Get(*ClientRequest, *ServerResponse) - Put(*ClientRequest, *ServerResponse) - Post(*ClientRequest, *ServerResponse) - Delete(*ClientRequest, *ServerResponse) - Head(*ClientRequest, *ServerResponse) - Patch(*ClientRequest, *ServerResponse) - Connect(*ClientRequest, *ServerResponse) - Options(*ClientRequest, *ServerResponse) - Trace(*ClientRequest, *ServerResponse) -} \ No newline at end of file diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 98646f8b7..8ef8ce39e 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -9,6 +9,8 @@ import ( "log" "sync" "errors" + "reflect" + "gitee.com/johng/gf/g/util/gutil" "gitee.com/johng/gf/g/container/gmap" ) @@ -30,8 +32,11 @@ type Server struct { // 域名、URI与回调函数的绑定记录表 type HandlerMap map[string]HandlerFunc -// http回调函数 -type HandlerFunc func(*ClientRequest, *ServerResponse) +// http回调函数信息 +type HandlerFunc struct { + ctype reflect.Type // 控制器类型 + fname string // 回调方法名称 +} // Server表,用以存储和检索名称与Server对象之间的关联关系 var serverMapping *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap() @@ -224,12 +229,12 @@ func (s *Server) setHandler(domain, method, pattern string, handler HandlerFunc) } // 查询请求处理方法 -func (s *Server) getHandler(domain, method, pattern string) HandlerFunc { +func (s *Server) getHandler(domain, method, pattern string) *HandlerFunc { s.hmu.RLock() defer s.hmu.RUnlock() key := s.handlerKey(domain, method, pattern) if f, ok := s.handlerMap[key]; ok { - return f + return &f } return nil } @@ -237,14 +242,10 @@ func (s *Server) getHandler(domain, method, pattern string) HandlerFunc { // 绑定URI到操作函数/方法 // pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn // 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { +func (s *Server)bindHandler(pattern string, handler HandlerFunc) error { if s.status == 1 { return errors.New("server handlers cannot be changed while running") } - - s.hmu.Lock() - defer s.hmu.Unlock() - uri := "" domain := gDEFAULT_DOMAIN method := "all" @@ -267,28 +268,41 @@ func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { } // 通过映射数组绑定URI到操作函数/方法 -func (s *Server)BindHandlerByMap(m HandlerMap) error { - for p, f := range m { - if err := s.BindHandler(p, f); err != nil { +func (s *Server)bindHandlerByMap(m HandlerMap) error { + for p, h := range m { + if err := s.bindHandler(p, h); err != nil { return err } } return nil } -// 绑定控制器,控制器需要继承gmvc.Controller对象并实现需要的REST方法 -func (s *Server)BindControllerRest(uri string, c ControllerRest) error { - return s.BindHandlerByMap(HandlerMap{ - "GET:" + uri : c.Get, - "PUT:" + uri : c.Put, - "POST:" + uri : c.Post, - "DELETE:" + uri : c.Delete, - "PATCH:" + uri : c.Patch, - "HEAD:" + uri : c.Head, - "CONNECT:" + uri : c.Connect, - "OPTIONS:" + uri : c.Options, - "TRACE:" + uri : c.Trace, - }) +// 绑定方法,pattern支持http method +// pattern的格式形如:/user/list, put:/user, delete:/user +func (s *Server)BindMethod(pattern string, c Controller, method string) error { + return s.bindHandler(pattern, HandlerFunc{reflect.ValueOf(c).Type(), method}) } - +// 绑定控制器,控制器需要实现gmvc.Controller接口 +func (s *Server)BindController(uri string, c Controller) error { + // 遍历控制器,获取方法列表,并构造成uri + m := make(HandlerMap) + v := reflect.ValueOf(c) + t := v.Type() + for i := 0; i < v.NumMethod(); i++ { + key := strings.TrimRight(uri, "/") + "/" + name := t.Method(i).Name + if name == "Init" || name == "Shut" { + continue + } + for i := 0; i < len(name); i++ { + if i > 0 && gutil.IsLetterUpper(name[i]) { + key += "-" + } + key += strings.ToLower(string(name[i])) + } + m[key] = HandlerFunc{t, name} + } + //fmt.Println(m) + return s.bindHandlerByMap(m) +} diff --git a/g/net/ghttp/http_server_cookie.go b/g/net/ghttp/http_server_cookie.go new file mode 100644 index 000000000..4d90e9f16 --- /dev/null +++ b/g/net/ghttp/http_server_cookie.go @@ -0,0 +1,75 @@ +package ghttp + +import ( + "sync" + "net/http" +) + +// cookie对象 +type Cookie struct { + mu sync.RWMutex // 并发安全互斥锁 + data map[string]CookieItem // 数据项 + request *ClientRequest // 所属HTTP请求对象 + response *ServerResponse // 所属HTTP返回对象 +} + +// cookie项 +type CookieItem struct { + value string + domain string + path string + maxage int +} + +// 初始化cookie对象 +func NewCookie(r *ClientRequest, w *ServerResponse) *Cookie { + return &Cookie{ + data : make(map[string]CookieItem), + request : r, + response : w, + } +} + +// 从请求流中初始化 +func (c *Cookie) init() { + c.mu.Lock() + defer c.mu.Unlock() + for _, v := range c.request.Cookies() { + c.data[v.Name] = CookieItem { + v.Value, v.Domain, v.Path, v.MaxAge, + } + } +} + +// 设置cookie +func (c *Cookie) Set(key, value, domain, path string, maxage int) { + c.mu.Lock() + defer c.mu.Unlock() + c.data[key] = CookieItem { + value, domain, path, maxage, + } +} + +func (c *Cookie) Get(key string) string { + c.mu.RLock() + defer c.mu.RUnlock() + if r, ok := c.data[key]; ok { + return r.value + } + return "" +} + +func (c *Cookie) Remove(key string) { + c.mu.Lock() + defer c.mu.Unlock() + delete(c.data, key) +} + +// 输出到客户端 +func (c *Cookie) Output() { + c.mu.RLock() + defer c.mu.RUnlock() + for k, v := range c.data { + http.SetCookie(c.response.ResponseWriter, &http.Cookie{Name: k, Value: v.value, Domain: v.domain, Path: v.path, MaxAge: v.maxage}) + } +} diff --git a/g/net/ghttp/http_server_domain.go b/g/net/ghttp/http_server_domain.go index f8d281634..a521a2a1e 100644 --- a/g/net/ghttp/http_server_domain.go +++ b/g/net/ghttp/http_server_domain.go @@ -31,30 +31,15 @@ func (s *Server) Domain(domain string) *Domain { return d } -// 在当前域名中绑定回调函数 -func (d *Domain) BindHandler(pattern string, handler HandlerFunc) error { - for domain, _ := range d.m { - if err := d.s.BindHandler(pattern + "@" + domain, handler); err != nil { - return err - } - } - return nil +// 绑定方法 +func (d *Domain) BindMethod(pattern string, c Controller, method string) error { + return d.s.BindMethod(pattern, c, method) } -// 批量绑定 -func (d *Domain) BindHandlerByMap(m HandlerMap) error { - for p, f := range m { - if err := d.s.BindHandler(p, f); err != nil { - return err - } - } - return nil -} - -// 绑定REST控制器 -func (d *Domain) BindControllerRest(uri string, c ControllerRest) error { +// 绑定控制器 +func (d *Domain) BindController(uri string, c Controller) error { for domain, _ := range d.m { - if err := d.s.BindControllerRest(uri + "@" + domain, c); err != nil { + if err := d.s.BindController(uri + "@" + domain, c); err != nil { return err } } diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index 30847bd82..b0ce8702b 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -10,6 +10,7 @@ import ( "path/filepath" "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/encoding/ghtml" + "reflect" ) // 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行 @@ -19,21 +20,32 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) { // 执行处理HTTP请求 func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { - request := ClientRequest{} - response := ServerResponse {server : s} + request := &ClientRequest{} + response := &ServerResponse {server : s} request.Request = *r response.ResponseWriter = w - if f := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); f != nil { - f(&request, &response) + if h := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); h != nil { + s.initController(h, request, response) } else { - if f := s.getHandler(strings.Split(r.Host, ":")[0], r.Method, r.URL.Path); f != nil { - f(&request, &response) + if h := s.getHandler(strings.Split(r.Host, ":")[0], r.Method, r.URL.Path); h != nil { + s.initController(h, request, response) } else { s.serveFile(w, r) } } } +// 初始化控制器 +func (s *Server)initController(h *HandlerFunc, r *ClientRequest, w *ServerResponse) { + c := reflect.Indirect(reflect.New(h.ctype)).Elem() + fmt.Println(c.String()) + c.FieldByName("Request").Set(reflect.ValueOf(r)) + c.FieldByName("Response").Set(reflect.ValueOf(w)) + c.MethodByName("Init").Call(nil) + c.MethodByName(h.fname).Call(nil) + c.MethodByName("Shut").Call(nil) +} + // 处理静态文件请求 func (s *Server)serveFile(w http.ResponseWriter, r *http.Request) { uri := r.URL.String() diff --git a/g/net/gsession/gsession.go b/g/net/gsession/gsession.go index 44d2f8b86..408ccea20 100644 --- a/g/net/gsession/gsession.go +++ b/g/net/gsession/gsession.go @@ -47,45 +47,40 @@ func cacheKey(sessionid string) string { // 获取sessionid func (s *Session) Id () string { - s.mu.RLock() - defer s.mu.RUnlock() - id := s.id - s.updateExpire() - return id + go s.updateExpire() + return s.id } // 获取当前session所有数据 func (s *Session) Data () map[string]interface{} { - m := *s.data.Clone() - s.updateExpire() - return m + go s.updateExpire() + return *s.data.Clone() } -// 设置session过期间隔 +// 设置session过期间隔(秒) func (s *Session) SetExpire (expire int) { s.mu.Lock() defer s.mu.Unlock() + go s.updateExpire() s.expire = expire - s.updateExpire() } // 设置session func (s *Session) Set (k string, v interface{}) { + go s.updateExpire() s.data.Set(k, v) - s.updateExpire() } // 获取session func (s *Session) Get (k string) interface{} { - r := s.data.Get(k) - s.updateExpire() - return r + go s.updateExpire() + return s.data.Get(k) } // 删除session func (s *Session) Remove (k string) { + go s.updateExpire() s.data.Remove(k) - s.updateExpire() } // 更新过期时间 diff --git a/g/util/gutil/util.go b/g/util/gutil/util.go index 3a1565b58..93d27716d 100644 --- a/g/util/gutil/util.go +++ b/g/util/gutil/util.go @@ -15,5 +15,19 @@ func StringInArray (a []string, s string) bool { return StringSearch(a, s) != -1 } +// 判断给定字符是否小写 +func IsLetterLower(b byte) bool { + if b >= byte('a') && b <= byte('z') { + return true + } + return false +} +// 判断给定字符是否大写 +func IsLetterUpper(b byte) bool { + if b >= byte('A') && b <= byte('Z') { + return true + } + return false +} diff --git a/geg/frame/mvc/controller/user/user.go b/geg/frame/mvc/controller/user/user.go index 81d3cd525..9a095fd17 100644 --- a/geg/frame/mvc/controller/user/user.go +++ b/geg/frame/mvc/controller/user/user.go @@ -2,14 +2,11 @@ package user import ( "gitee.com/johng/gf/g/net/ghttp" - "html/template" - "fmt" - "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/frame/gmvc" ) // 定义业务相关的控制器对象 -type Controller_User struct { +type ControllerUser struct { gmvc.Controller } @@ -19,22 +16,21 @@ func Add(i1, i2 int) int { // 初始化控制器对象,并绑定操作到Web Server func init() { - u := &Controller_User{} - ghttp.GetServer("johng").Domain("localhost").BindHandler("/user/info", u.Info) + //ghttp.GetServer("johng").Domain("localhost").BindHandler("/user", u.Info) + ghttp.GetServer("johng").BindController("/user", &ControllerUser{}) } // 定义操作逻辑 -func (cu *Controller_User) Info(r *ghttp.ClientRequest, w *ghttp.ServerResponse) { - r.c - //w.Write([]byte("user information page")) - t, err := template.New("test").Funcs(template.FuncMap{"add": Add}).Parse(gfile.GetContents("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/info.tpl")) - if err != nil { - fmt.Println(err) - } - - t.Execute(w.ResponseWriter, map[string]string{ - "name" : "john", - }) +func (cu *ControllerUser) Info() { + cu.Response.Write([]byte("user information page")) + //t, err := template.New("test").Funcs(template.FuncMap{"add": Add}).Parse(gfile.GetContents("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/info.tpl")) + //if err != nil { + // fmt.Println(err) + //} + // + //t.Execute(w.ResponseWriter, map[string]string{ + // "name" : "john", + //}) } diff --git a/geg/other/test.go b/geg/other/test.go index 4fa303675..d7a7b5a46 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,26 +1,28 @@ package main import ( - "time" "fmt" - "gitee.com/johng/gf/g/frame/gmvc" - "gitee.com/johng/gf/g/net/gsession" ) -type User struct { - Username, Password string - RegTime time.Time + +type B struct { + Name string } -func add(i1, i2 int) int { - return i1 + i2 + 1 + +func (b B) Get() { + fmt.Printf("b addr:%p\n", &b) } func main() { - fmt.Println(gsession.Id()) - view := gmvc.NewView("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/") - tpl, _ := view.Template("info") - tpl.BindFunc("add", add) - fmt.Println(tpl.Parse(nil)) - //t, err := template.New("text").Funcs(template.FuncMap{"add":add}).Parse(`{{add 1 2}}`) - //if err != nil { - // panic(err) - //} - //t.Execute(os.Stdout, u) + b := B{} + b.Get() + b.Get() + + return + //view := gmvc.NewView("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/frame/mvc/view/user/") + //tpl, _ := view.Template("info") + //tpl.BindFunc("add", add) + //fmt.Println(tpl.Parse(nil)) + ////t, err := template.New("text").Funcs(template.FuncMap{"add":add}).Parse(`{{add 1 2}}`) + ////if err != nil { + //// panic(err) + ////} + ////t.Execute(os.Stdout, u) } \ No newline at end of file