mirror of
https://gitee.com/johng/gf
synced 2026-06-25 01:05:41 +08:00
ghttp server增加多域名支持,gmvc增加视图支持
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
package mvc
|
||||
package gmvc
|
||||
|
||||
import "gitee.com/johng/gf/g/net/ghttp"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package mvc
|
||||
package gmvc
|
||||
|
||||
import "gitee.com/johng/gf/g/net/ghttp"
|
||||
|
||||
1
g/frame/gmvc/model.go
Normal file
1
g/frame/gmvc/model.go
Normal file
@ -0,0 +1 @@
|
||||
package gmvc
|
||||
115
g/frame/gmvc/view.go
Normal file
115
g/frame/gmvc/view.go
Normal file
@ -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
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
package mvc
|
||||
@ -1 +0,0 @@
|
||||
package mvc
|
||||
@ -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,
|
||||
|
||||
62
g/net/ghttp/http_server_domain.go
Normal file
62
g/net/ghttp/http_server_domain.go
Normal file
@ -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
|
||||
}
|
||||
@ -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)
|
||||
@ -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",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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"))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
1
geg/frame/mvc/view/user/footer.tpl
Normal file
1
geg/frame/mvc/view/user/footer.tpl
Normal file
@ -0,0 +1 @@
|
||||
<div>footer</div>
|
||||
10
geg/frame/mvc/view/user/info.tpl
Normal file
10
geg/frame/mvc/view/user/info.tpl
Normal file
@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>age:{{.}}</p>
|
||||
<p>add:{{add 1 2}}</p>
|
||||
</body>
|
||||
</html>
|
||||
@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user