From 7fecb2a9f03ddf8d3566c6aadf59d471a6d9ce4c Mon Sep 17 00:00:00 2001 From: John Date: Mon, 8 Jan 2018 17:07:05 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E6=A8=A1=E6=9D=BF=E5=BC=95?= =?UTF-8?q?=E6=93=8E=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=AE=8C=E5=96=84=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=BC=95=E6=93=8E=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/frame/gmvc/view.go | 30 +--- g/os/gview/gview.go | 155 +++++++++------------ geg/frame/mvc/controller/demo/template.go | 5 +- geg/frame/mvc/controller/demo/template2.go | 4 +- geg/frame/mvc/controller/demo/template3.go | 17 +++ geg/frame/mvc/view/user/index.tpl | 2 +- 6 files changed, 89 insertions(+), 124 deletions(-) create mode 100644 geg/frame/mvc/controller/demo/template3.go diff --git a/g/frame/gmvc/view.go b/g/frame/gmvc/view.go index 49799ec23..43853a413 100644 --- a/g/frame/gmvc/view.go +++ b/g/frame/gmvc/view.go @@ -8,10 +8,9 @@ package gmvc import ( "sync" - "html/template" "gitee.com/johng/gf/g/os/gview" - "gitee.com/johng/gf/g/frame/gins" "gitee.com/johng/gf/g/net/ghttp" + "gitee.com/johng/gf/g/frame/gins" ) // MVC视图基类(一个请求一个视图对象,用完即销毁) @@ -49,23 +48,15 @@ func (view *View) Assign(key string, value interface{}) { // 解析模板,并返回解析后的内容 func (view *View) Parse(file string) ([]byte, error) { - // 查询模板 - tpl, err := view.view.Template(file) - if err != nil { - return nil, err - } - // 绑定函数,对基类的include进行覆盖(由于涉及到模板变量的传递) - tpl.BindFunc("include", view.funcInclude) - // 执行解析 view.mu.RLock() - content, err := tpl.Parse(view.data) + content, err := view.view.Parse(file, view.data) view.mu.RUnlock() return content, err } // 解析指定模板 func (view *View) Display(files...string) error { - file := "default" + file := "index.tpl" if len(files) > 0 { file = files[0] } @@ -76,17 +67,4 @@ func (view *View) Display(files...string) error { view.response.Write(content) } return nil -} - -// 模板内置方法:include -func (view *View) funcInclude(file string) template.HTML { - tpl, err := view.view.Template(file) - if err != nil { - return template.HTML(err.Error()) - } - content, err := tpl.Parse(view.data) - if err != nil { - return template.HTML(err.Error()) - } - return template.HTML(content) -} +} \ No newline at end of file diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go index 4a53d9033..435417714 100644 --- a/g/os/gview/gview.go +++ b/g/os/gview/gview.go @@ -8,30 +8,21 @@ package gview import ( - "gitee.com/johng/gf/g/container/gmap" - "html/template" - "gitee.com/johng/gf/g/os/gfile" "sync" - "strings" "bytes" "errors" + "strings" + "html/template" + "gitee.com/johng/gf/g/os/gfile" + "gitee.com/johng/gf/g/container/gmap" ) // 视图对象 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 // 模板文件内容(解析之后保存到内存中) - funcmap map[string]interface{} // FuncMap + mu sync.RWMutex + path string // 模板目录(绝对路径) + funcmap map[string]interface{} // FuncMap + contents *gmap.StringStringMap // 已解析的模板文件内容 } // 视图表 @@ -49,11 +40,13 @@ func Get(path string) *View { // 生成一个视图对象 func New(path string) *View { - return &View { - path : path, - tpls : gmap.NewStringInterfaceMap(), - suffix : "tpl", + view := &View { + path : path, + funcmap : make(map[string]interface{}), + contents : gmap.NewStringStringMap(), } + view.BindFunc("include", view.funcInclude) + return view } // 设置模板目录绝对路径 @@ -70,89 +63,67 @@ func (view *View) GetPath() string { return view.path } +// 获取模板文件内容 +func (view *View) GetTplContent() string { + view.mu.RLock() + defer view.mu.RUnlock() + return view.path +} + // 直接解析模板,返回解析后的内容 func (view *View) Parse(file string, params map[string]interface{}) ([]byte, error) { - if t, err := view.Template(file); err == nil { - return t.Parse(params) - } else { - return nil, err - } -} - -// 根据文件名称生成一个模板对象,或者获取一个现有的模板对象 -func (view *View) Template(file string) (*Template, error) { - path := strings.TrimRight(view.GetPath(), gfile.Separator) + gfile.Separator + file + "." + view.suffix - 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), - funcmap : make(map[string]interface{}), - } - // 绑定内置inluce方法 - t.BindFunc("include", t.funcInclude) - // 将模板对象注册到内部map中 - view.tpls.Set(path, t) - return t, nil -} - -// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制 -func (t *Template) BindFunc(name string, function interface{}) { - t.mu.Lock() - defer t.mu.Unlock() - t.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 -// 函数内部的底层template必须每次调用都新生成一个,防止错误:html/template: cannot Parse after Execute -func (t *Template) Parse(data map[string]interface{}) ([]byte, error) { - t.mu.RLock() - defer t.mu.RUnlock() - buffer := bytes.NewBuffer(nil) - if tpl, err := template.New(t.path).Funcs(t.funcmap).Parse(t.content); err != nil { - return nil, err - } else { - m := t.data - for k, v := range data { - m[k] = v + // 获取模板文件路径及内容 + path := strings.TrimRight(view.GetPath(), gfile.Separator) + gfile.Separator + file + content := view.contents.Get(path) + if content == "" { + content = gfile.GetContents(path) + if content == "" { + content = gfile.GetContents(file) } - if err := tpl.Execute(buffer, m); err != nil { + view.contents.Set(path, content) + } + if content == "" { + return nil, errors.New("invalid tpl \"" + file + "\"") + } + // 执行模板解析 + buffer := bytes.NewBuffer(nil) + if tpl, err := template.New(path).Funcs(view.getFuncs()).Parse(content); err != nil { + return nil, err + } else { + if err := tpl.Execute(buffer, params); err != nil { return nil, err } } return buffer.Bytes(), nil } +// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制 +func (view *View) BindFunc(name string, function interface{}) { + view.mu.Lock() + defer view.mu.Unlock() + view.funcmap[name] = function +} + +// 获取模板自定义函数,每一次都是一份拷贝 +func (view *View) getFuncs() map[string]interface{} { + m := make(map[string]interface{}) + view.mu.RLock() + for k, v := range view.funcmap { + m[k] = v + } + view.mu.RUnlock() + return m +} + // 模板内置方法:include -func (t *Template) funcInclude(file string) template.HTML { - content, err := t.Parse(nil) +func (view *View) funcInclude(file string, datas...map[string]interface{}) template.HTML { + var data map[string]interface{} = nil + if len(datas) > 0 { + data = datas[0] + } + content, err := view.Parse(file, data) if err != nil { return template.HTML(err.Error()) } return template.HTML(content) } - diff --git a/geg/frame/mvc/controller/demo/template.go b/geg/frame/mvc/controller/demo/template.go index 316fcc1a4..825ccb29e 100644 --- a/geg/frame/mvc/controller/demo/template.go +++ b/geg/frame/mvc/controller/demo/template.go @@ -3,6 +3,7 @@ package demo import ( "gitee.com/johng/gf/g/net/ghttp" "gitee.com/johng/gf/g/frame/gmvc" + "gitee.com/johng/gf/g/frame/gins" ) type ControllerTemplate struct { @@ -10,7 +11,7 @@ type ControllerTemplate struct { } func init() { - ghttp.GetServer().BindControllerMethod("/template/info", &ControllerTemplate{}, "Info") + ghttp.GetServer().BindControllerMethod("/template", &ControllerTemplate{}, "Info") } func (c *ControllerTemplate) Info() { @@ -19,7 +20,7 @@ func (c *ControllerTemplate) Info() { "age" : 18, "score" : 100, }) - c.View.Display("user/index") + c.View.Display("user/index.tpl") } diff --git a/geg/frame/mvc/controller/demo/template2.go b/geg/frame/mvc/controller/demo/template2.go index 783540a34..99aa19b32 100644 --- a/geg/frame/mvc/controller/demo/template2.go +++ b/geg/frame/mvc/controller/demo/template2.go @@ -7,9 +7,7 @@ import ( func init() { ghttp.GetServer().BindHandler("/template2", func(r *ghttp.Request){ - view := gins.View() - view.SetPath("/home/www/template/") - content, _ := view.Parse("index", map[string]interface{}{ + content, _ := gins.View().Parse("index.tpl", map[string]interface{}{ "id" : 123, "name" : "john", }) diff --git a/geg/frame/mvc/controller/demo/template3.go b/geg/frame/mvc/controller/demo/template3.go new file mode 100644 index 000000000..550526fe2 --- /dev/null +++ b/geg/frame/mvc/controller/demo/template3.go @@ -0,0 +1,17 @@ +package demo + +import ( + "gitee.com/johng/gf/g/net/ghttp" + "gitee.com/johng/gf/g/frame/gins" +) + +func init() { + gins.View().SetPath("/home/www/template/") + ghttp.GetServer().BindHandler("/template3", func(r *ghttp.Request){ + content, _ := gins.View().Parse("index.tpl", map[string]interface{}{ + "id" : 123, + "name" : "john", + }) + r.Response.Write(content) + }) +} \ No newline at end of file diff --git a/geg/frame/mvc/view/user/index.tpl b/geg/frame/mvc/view/user/index.tpl index afd5aeb51..a6d899ec9 100644 --- a/geg/frame/mvc/view/user/index.tpl +++ b/geg/frame/mvc/view/user/index.tpl @@ -6,6 +6,6 @@

This is index

tpl vals: {{.}}

- {{include "user/footer"}} + {{include "user/footer.tpl" }} \ No newline at end of file