diff --git a/g/frame/mvc/controller.go b/g/frame/gmvc/controller.go similarity index 89% rename from g/frame/mvc/controller.go rename to g/frame/gmvc/controller.go index 02fc204e8..4ccf347d6 100644 --- a/g/frame/mvc/controller.go +++ b/g/frame/gmvc/controller.go @@ -1,4 +1,4 @@ -package mvc +package gmvc import "gitee.com/johng/gf/g/net/ghttp" diff --git a/g/frame/mvc/controller_rest.go b/g/frame/gmvc/controller_rest.go similarity index 91% rename from g/frame/mvc/controller_rest.go rename to g/frame/gmvc/controller_rest.go index e69517352..9b640ad41 100644 --- a/g/frame/mvc/controller_rest.go +++ b/g/frame/gmvc/controller_rest.go @@ -1,4 +1,4 @@ -package mvc +package gmvc import "gitee.com/johng/gf/g/net/ghttp" diff --git a/g/frame/gmvc/model.go b/g/frame/gmvc/model.go new file mode 100644 index 000000000..54fc4f23f --- /dev/null +++ b/g/frame/gmvc/model.go @@ -0,0 +1 @@ +package gmvc diff --git a/g/frame/gmvc/view.go b/g/frame/gmvc/view.go new file mode 100644 index 000000000..831035b75 --- /dev/null +++ b/g/frame/gmvc/view.go @@ -0,0 +1,115 @@ +package gmvc + +import ( + "gitee.com/johng/gf/g/container/gmap" + "html/template" + "gitee.com/johng/gf/g/os/gfile" + "sync" + "strings" + "bytes" + "errors" +) + +// 视图对象 +type View struct { + mu sync.RWMutex + path string // 模板目录(绝对路径) + tpls *gmap.StringInterfaceMap // 已解析的模板对象指针,防止重复解析 + suffix string // 模板文件名后缀 +} + +// 模板对象 +type Template struct { + mu sync.RWMutex // 并发互斥锁 + path string // 模板文件(绝对路径) + data map[string]interface{} // 全局的模板变量 + content string // 模板内容(解析之后保存到内存中) + template *template.Template // 底层模板对象 + lasterror error // 最近一次错误 +} + +// 生成一个视图对象 +func NewView(path string) *View { + return &View{ + path : path, + tpls : gmap.NewStringInterfaceMap(), + suffix : "tpl", + } +} + +// 设置模板文件后缀名 +func (view *View) SetSuffix(suffix string) { + view.mu.Lock() + defer view.mu.Unlock() + view.suffix = suffix +} + +// 获取模板文件后缀名 +func (view *View) GetSuffix() string { + view.mu.Lock() + defer view.mu.Unlock() + return view.suffix +} + +// 根据文件名称生成一个模板对象,或者获取一个现有的模板对象 +func (view *View) Template(file string) (*Template, error) { + path := strings.TrimRight(view.path, gfile.Separator) + gfile.Separator + file + "." + view.GetSuffix() + if t := view.tpls.Get(path); t != nil { + return t.(*Template), nil + } + if !gfile.Exists(path) { + return nil, errors.New("template '" + path + "' does not exist") + } + if !gfile.IsReadable(path) { + return nil, errors.New("template '" + path + "' is not readable") + } + t := &Template{ + path : path, + data : make(map[string]interface{}), + content : gfile.GetContents(path), + template : template.New(path), + } + view.tpls.Set(path, t) + return t, nil +} + +// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制 +func (t *Template) BindFunc(name string, function interface{}) { + t.mu.Lock() + defer t.mu.Unlock() + t.template.Funcs(template.FuncMap{name : function}) +} + +// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制 +func (t *Template) Assigns(data map[string]interface{}) { + t.mu.Lock() + defer t.mu.Unlock() + for k, v := range data { + t.data[k] = v + } +} + +// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制 +func (t *Template) Assign(k string, v interface{}) { + t.mu.Lock() + defer t.mu.Unlock() + t.data[k] = v +} + +// 返回解析后的模板内容,可以额外指定模板变量,如果没有可以传入nil +func (t *Template) Parse(data map[string]interface{}) (string, error) { + buffer := bytes.NewBuffer(nil) + if tpl, err := t.template.Parse(t.content); err != nil { + return "", err + } else { + m := t.data + for k, v := range data { + m[k] = v + } + if err := tpl.Execute(buffer, m); err != nil { + return "", err + } + } + return buffer.String(), nil +} + diff --git a/g/frame/mvc/model.go b/g/frame/mvc/model.go deleted file mode 100644 index 317536536..000000000 --- a/g/frame/mvc/model.go +++ /dev/null @@ -1 +0,0 @@ -package mvc diff --git a/g/frame/mvc/view.go b/g/frame/mvc/view.go deleted file mode 100644 index 317536536..000000000 --- a/g/frame/mvc/view.go +++ /dev/null @@ -1 +0,0 @@ -package mvc diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 6fc184da3..2007ff326 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -12,6 +12,11 @@ import ( "gitee.com/johng/gf/g/container/gmap" ) +const ( + gDEFAULT_DOMAIN = "default" + gDEFAULT_METHOD = "all" +) + // http server结构体 type Server struct { hmu sync.RWMutex // handlerMap互斥锁 @@ -22,12 +27,12 @@ type Server struct { status int8 // 当前服务器状态(0:未启动,1:运行中) } +// 域名、URI与回调函数的绑定记录表 +type HandlerMap map[string]HandlerFunc + // http回调函数 type HandlerFunc func(*ClientRequest, *ServerResponse) -// uri与回调函数的绑定记录表 -type HandlerMap map[string]HandlerFunc - // Server表,用以存储和检索名称与Server对象之间的关联关系 var serverMapping *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap() @@ -36,8 +41,9 @@ func GetServer(name string) (*Server) { if s := serverMapping.Get(name); s != nil { return s.(*Server) } - s := &Server{} - s.name = name + s := &Server{} + s.name = name + s.handlerMap = make(HandlerMap) s.SetConfig(defaultServerConfig) serverMapping.Set(name, s) return s @@ -193,45 +199,82 @@ func (s *Server)SetServerRoot(root string) error { return nil } -// 绑定URI到操作函数/方法 -// pattern的格式形如:/user/list, put:/user, delete:/user -// 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server)BindHandle(pattern string, handler HandlerFunc) error { - if s.status == 1 { - return errors.New("server handlers cannot be changed while running") - } +// 生成回调方法查询的Key +func (s *Server) handlerKey(domain, method, pattern string) string { + return strings.ToUpper(method) + ":" + pattern + "@" + strings.ToLower(domain) +} +// 设置请求处理方法 +func (s *Server) setHandler(domain, method, pattern string, handler HandlerFunc) { s.hmu.Lock() defer s.hmu.Unlock() + if method == gDEFAULT_METHOD { + s.handlerMap[s.handlerKey(domain, "GET", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "PUT", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "POST", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "DELETE", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "PATCH", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "HEAD", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "CONNECT", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "OPTIONS", pattern)] = handler + s.handlerMap[s.handlerKey(domain, "TRACE", pattern)] = handler + } else { + s.handlerMap[s.handlerKey(domain, method, pattern)] = handler + } +} - if s.handlerMap == nil { - s.handlerMap = make(HandlerMap) - } - key := "" - result := strings.Split(pattern, ":") - if len(result) > 1 { - key = strings.ToUpper(result[0]) + ":" + result[1] - } else { - key = strings.TrimSpace(pattern) - } - if _, ok := s.handlerMap[key]; ok { - return errors.New("duplicated http server handler for: " + pattern) - } else { - s.handlerMap[key] = handler +// 查询请求处理方法 +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 nil } -// 通过映射数组绑定URI到操作函数/方法 -func (s *Server)BindHandleByMap(m HandlerMap) { - for p, f := range m { - s.BindHandle(p, f) +// 绑定URI到操作函数/方法 +// pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn +// 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 +func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { + if s.status == 1 { + return errors.New("server handlers cannot be changed while running") } + uri := "" + domain := gDEFAULT_DOMAIN + method := gDEFAULT_METHOD + result := strings.Split(pattern, "@") + if len(result) > 1 { + domain = result[1] + } + result = strings.Split(result[0], ":") + if len(result) > 1 { + method = result[0] + uri = result[0] + } else { + uri = result[0] + } + if uri == "" { + return errors.New("invalid pattern") + } + s.setHandler(domain, method, uri, handler) + return nil } -// 绑定控制器,控制器需要继承gmvc.ControllerBase对象并实现需要的REST方法 -func (s *Server)BindControllerRest(uri string, c ControllerRest) { - s.BindHandleByMap(HandlerMap{ +// 通过映射数组绑定URI到操作函数/方法 +func (s *Server)BindHandlerByMap(m HandlerMap) error { + for p, f := range m { + if err := s.BindHandler(p, f); 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, diff --git a/g/net/ghttp/http_server_domain.go b/g/net/ghttp/http_server_domain.go new file mode 100644 index 000000000..f8d281634 --- /dev/null +++ b/g/net/ghttp/http_server_domain.go @@ -0,0 +1,62 @@ +package ghttp + +import ( + "strings" + "gitee.com/johng/gf/g/container/gmap" +) + +// 域名管理器对象 +type Domain struct { + s *Server // 所属Server + m map[string]bool // 多域名 +} + +// 域名对象表,用以存储和检索域名(支持多域名)与域名对象之间的关联关系 +var domains *gmap.StringInterfaceMap = gmap.NewStringInterfaceMap() + +// 生成一个域名对象 +func (s *Server) Domain(domain string) *Domain { + if r := domains.Get(domain); r != nil { + return r.(*Domain) + } + d := &Domain{ + s : s, + m : make(map[string]bool), + } + result := strings.Split(domain, ",") + for _, v := range result { + d.m[strings.TrimSpace(v)] = true + } + domains.Set(domain, d) + 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) 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 { + for domain, _ := range d.m { + if err := d.s.BindControllerRest(uri + "@" + domain, c); err != nil { + return err + } + } + return nil +} \ No newline at end of file diff --git a/g/net/ghttp/http_server_handle.go b/g/net/ghttp/http_server_handler.go similarity index 94% rename from g/net/ghttp/http_server_handle.go rename to g/net/ghttp/http_server_handler.go index 3c489b01f..30847bd82 100644 --- a/g/net/ghttp/http_server_handle.go +++ b/g/net/ghttp/http_server_handler.go @@ -23,11 +23,10 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { response := ServerResponse {server : s} request.Request = *r response.ResponseWriter = w - if f, ok := s.handlerMap[r.URL.Path]; ok { + if f := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); f != nil { f(&request, &response) } else { - method := strings.ToUpper(r.Method) - if f, ok := s.handlerMap[method + ":" + r.URL.Path]; ok { + if f := s.getHandler(strings.Split(r.Host, ":")[0], r.Method, r.URL.Path); f != nil { f(&request, &response) } else { s.serveFile(w, r) diff --git a/geg/frame/mvc/controller/user/user.go b/geg/frame/mvc/controller/user/user.go index c3b92d377..0eabe379a 100644 --- a/geg/frame/mvc/controller/user/user.go +++ b/geg/frame/mvc/controller/user/user.go @@ -3,22 +3,37 @@ package user import ( "gitee.com/johng/gf/g/net/ghttp" "gitee.com/johng/gf/g/frame/mvc" + "html/template" + "fmt" + "gitee.com/johng/gf/g/os/gfile" ) // 定义业务相关的控制器对象 type Controller_User struct { - mvc.Controller + gmvc.Controller +} + +func Add(i1, i2 int) int { + return i1 + i2 } // 初始化控制器对象,并绑定操作到Web Server func init() { u := &Controller_User{} - ghttp.GetServer("johng.cn").BindHandle("/user/info", u.Info) + ghttp.GetServer("johng").Domain("localhost").BindHandler("/user/info", u.Info) } // 定义操作逻辑 func (cu *Controller_User) Info(r *ghttp.ClientRequest, w *ghttp.ServerResponse) { - w.Write([]byte("user information page")) + //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", + }) } diff --git a/geg/frame/mvc/controller/user/user_register.go b/geg/frame/mvc/controller/user/user_register.go deleted file mode 100644 index 85ac91dee..000000000 --- a/geg/frame/mvc/controller/user/user_register.go +++ /dev/null @@ -1,25 +0,0 @@ -package user - -import ( - "gitee.com/johng/gf/g/net/ghttp" - "gitee.com/johng/gf/g/frame/mvc" -) - -// 定义业务相关的控制器对象 -type Controller_User_Register struct { - mvc.Controller -} - -// 初始化控制器对象,并绑定操作到Web Server -func init() { - ur := &Controller_User_Register{} - ghttp.GetServer("johng.cn").BindHandle("/user/register", ur.Show) -} - -// 定义操作逻辑 -func (cu *Controller_User_Register) Show(r *ghttp.ClientRequest, w *ghttp.ServerResponse) { - w.Write([]byte("user register page")) -} - - - diff --git a/geg/frame/mvc/main.go b/geg/frame/mvc/main.go index 1d50119aa..2a9519edd 100644 --- a/geg/frame/mvc/main.go +++ b/geg/frame/mvc/main.go @@ -7,6 +7,6 @@ import ( ) func main() { - ghttp.GetServer("johng.cn").SetAddr(":8199") - ghttp.GetServer("johng.cn").Run() + ghttp.GetServer("johng").SetAddr(":8199") + ghttp.GetServer("johng").Run() } diff --git a/geg/frame/mvc/view/user/footer.tpl b/geg/frame/mvc/view/user/footer.tpl new file mode 100644 index 000000000..a79ba001f --- /dev/null +++ b/geg/frame/mvc/view/user/footer.tpl @@ -0,0 +1 @@ +
age:{{.}}
+add:{{add 1 2}}
+ + \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index fff367cb3..e1781029e 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,35 +1,24 @@ package main - import ( - "sync" - "fmt" - "strconv" "time" + "fmt" + "gitee.com/johng/gf/g/frame/gmvc" ) - - -type LockDemo struct { - var1 string - var2 string - mu1 sync.RWMutex - mu2 sync.RWMutex +type User struct { + Username, Password string + RegTime time.Time +} +func add(i1, i2 int) int { + return i1 + i2 + 1 } - func main() { - l := LockDemo{} - wg := sync.WaitGroup{} - for i := 0; i < 1000; i++ { - wg.Add(1) - go func(i int) { - l.mu1.Lock() - l.mu2.Lock() - defer l.mu2.Unlock() - defer l.mu1.Unlock() - l.var1 = strconv.Itoa(i) - l.var2 = strconv.Itoa(i + 1) - wg.Done() - }(i) - } - wg.Wait() - fmt.Println(l) + 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