完成mvc视图功能的开发及基本测试

This commit is contained in:
John
2017-12-13 16:45:00 +08:00
parent c205e5a335
commit a579d1715d
16 changed files with 319 additions and 163 deletions

View File

@ -0,0 +1,48 @@
// 全局配置管理对象
package gconfig
import (
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/encoding/gjson"
)
// 配置对象
var config = gmap.NewStringInterfaceMap()
// 获取配置
func Get(k string) interface{} {
return config.Get(k)
}
func GetInt(k string) int {
if v := config.Get(k); v != nil {
if r, ok := v.(int); ok {
return r
}
}
return 0
}
func GetString(k string) string {
if v := config.Get(k); v != nil {
if r, ok := v.(string); ok {
return r
}
}
return ""
}
// 适用于json文件配置在设置的时候通过gjson进行解析后再保存
func GetJson(k string) *gjson.Json {
if v := config.Get(k); v != nil {
if r, ok := v.(*gjson.Json); ok {
return r
}
}
return nil
}
// 设置配置
func Set(k string, v interface{}) {
config.Set(k, v)
}

View File

@ -1,8 +1,46 @@
// MVC控制器基类
package gmvc
import "gitee.com/johng/gf/g/net/ghttp"
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/net/gsession"
)
const (
gDEFAULT_SESSION_ID_NAME = "gfsessionid"
)
// 控制器基类
type Controller struct {
ghttp.ControllerBase
Server *ghttp.Server // Web Server对象
Request *ghttp.ClientRequest // 请求数据对象
Response *ghttp.ServerResponse // 返回数据对象
Cookie *ghttp.Cookie // COOKIE操作对象
Session *gsession.Session // SESSION操作对象
View *View // 视图对象
}
// 控制器初始化
func (c *Controller) Init(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse) {
c.Server = s
c.Request = r
c.Response = w
c.Cookie = ghttp.NewCookie(c.Request, c.Response)
c.View = NewView(c)
if r := c.Cookie.Get(gDEFAULT_SESSION_ID_NAME); r != "" {
c.Session = gsession.Get(r)
} else {
c.Session = gsession.Get(gsession.Id())
}
}
// 控制器结束请求
func (c *Controller) Shut() {
if c.Cookie.Get(gDEFAULT_SESSION_ID_NAME) == "" {
c.Cookie.Set(gDEFAULT_SESSION_ID_NAME, c.Session.Id())
}
c.Cookie.Output()
c.Response.Output()
}

View File

@ -1,115 +1,78 @@
package gmvc
import (
"gitee.com/johng/gf/g/container/gmap"
"html/template"
"gitee.com/johng/gf/g/os/gfile"
"sync"
"strings"
"bytes"
"errors"
"html/template"
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/frame/gconfig"
)
// 视图对象
// 视图对象(一个请求一个视图对象,用完即销毁)
type View struct {
mu sync.RWMutex
path string // 模板目录(绝对路径)
tpls *gmap.StringInterfaceMap // 已解析的模板对象指针,防止重复解析
suffix string // 模板文件名后缀
mu sync.RWMutex // 并发互斥锁
ctl *Controller // 所属控制器
view *gview.View // 底层视图对象
data map[string]interface{} // 视图数据
}
// 模板对象
type Template struct {
mu sync.RWMutex // 并发互斥锁
path string // 模板文件(绝对路径)
data map[string]interface{} // 全局的模板变量
content string // 模板内容(解析之后保存到内存中)
template *template.Template // 底层模板对象
lasterror error // 最近一次错误
}
// 生成一个视图对象
func NewView(path string) *View {
// 创建一个MVC请求中使用的视图对象
func NewView(c *Controller) *View {
path := c.Server.GetName() + ".gf.mvc.view.path"
return &View{
path : path,
tpls : gmap.NewStringInterfaceMap(),
suffix : "tpl",
ctl : c,
view : gview.GetView(gconfig.GetString(path)),
data : make(map[string]interface{}),
}
}
// 设置模板文件后缀名
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()
func (view *View) Assigns(data map[string]interface{}) {
view.mu.Lock()
defer view.mu.Unlock()
for k, v := range data {
t.data[k] = v
view.data[k] = v
}
}
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
func (t *Template) Assign(k string, v interface{}) {
t.mu.Lock()
defer t.mu.Unlock()
t.data[k] = v
func (view *View) Assign(key string, value interface{}) {
view.mu.Lock()
defer view.mu.Unlock()
view.data[key] = value
}
// 返回解析后的模板内容可以额外指定模板变量如果没有可以传入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
}
// 解析指定模板
func (view *View) Display(file string) error {
// 查询模板
tpl, err := view.view.Template(file)
if err != nil {
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
return err
}
return buffer.String(), nil
// 绑定函数
tpl.BindFunc("include", view.funcInclude)
// 执行解析
view.mu.RLock()
defer view.mu.RUnlock()
content, err := tpl.Parse(view.data)
if err != nil {
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
return err
} else {
view.ctl.Response.WriteString(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)
}