完成路由管理器

This commit is contained in:
John
2017-12-28 15:21:25 +08:00
parent e35aea0dae
commit 6fb3366edd
13 changed files with 242 additions and 48 deletions

View File

@ -13,7 +13,7 @@ import "gitee.com/johng/gf/g/xxx/xxx"
## 说明
.
├── g 【框架目录】
│   ├── container 【常用数据结构容器
│   ├── container 【常用数据结构】
│   │   ├── gbtree B+树
│   │   ├── glist 并发安全的双向链表
│   │   ├── gmap 并发安全的哈希表
@ -42,6 +42,7 @@ import "gitee.com/johng/gf/g/xxx/xxx"
│   ├── net 【网络通信】
│   │   ├── ghttp HTTP客户端及服务端
│   │   ├── gipv4 IP操作
│   │   ├── grouter 路由管理器
│   │   ├── gscanner 端口扫描
│   │   ├── gsession SESSION会话管理
│   │   ├── gsmtp SMTP邮件管理

View File

@ -4,15 +4,19 @@ package ginstance
import (
"strconv"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/genv"
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gconsole"
"gitee.com/johng/gf/g/database/gdb"
"gitee.com/johng/gf/g/frame/gconfig"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/net/ghttp"
)
const (
FRAME_CORE_COMPONENT_NAME_VIEW = "gf.component.view"
FRAME_CORE_COMPONENT_NAME_CONFIG = "gf.component.config"
FRAME_CORE_COMPONENT_NAME_DATABASE = "gf.component.database"
)
@ -30,6 +34,36 @@ func Set(k string, v interface{}) {
instances.Set(k, v)
}
// 核心对象Server
// 框架支持多服务器对象通过传入不同的name进行区分
func Server(names...string) *ghttp.Server {
name := "default"
if len(names) > 0 {
name = names[0]
}
return ghttp.GetServer(name)
}
// 核心对象View
func View() *gview.View {
result := Get(FRAME_CORE_COMPONENT_NAME_VIEW)
if result != nil {
return result.(*gview.View)
} else {
path := gconsole.Option.Get("viewpath")
if path == "" {
path = genv.Get("viewpath")
if path == "" {
path = gfile.SelfDir()
}
}
view := gview.GetView(path)
Set(FRAME_CORE_COMPONENT_NAME_VIEW, view)
return view
}
return nil
}
// 核心对象Config
// 配置文件目录查找依次为启动参数cfgpath、当前程序运行目录
func Config() *gconfig.Config {
@ -39,7 +73,10 @@ func Config() *gconfig.Config {
} else {
path := gconsole.Option.Get("cfgpath")
if path == "" {
path = gfile.SelfDir()
path = genv.Get("cfgpath")
if path == "" {
path = gfile.SelfDir()
}
}
config := gconfig.New(path)
Set(FRAME_CORE_COMPONENT_NAME_CONFIG, config)
@ -58,7 +95,6 @@ func Database(names...string) gdb.Link {
if config == nil {
return nil
}
if m := config.GetMap("database"); m != nil {
for group, v := range m {
if list, ok := v.([]interface{}); ok {

View File

@ -40,7 +40,6 @@ func (c *Controller) Shut() {
c.Cookie.Set(gDEFAULT_SESSION_ID_NAME, c.Session.Id())
}
c.Cookie.Output()
c.Response.Output()
}

View File

@ -4,8 +4,6 @@ import (
"sync"
"html/template"
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gconsole"
"gitee.com/johng/gf/g/frame/ginstance"
)
@ -19,19 +17,9 @@ type View struct {
// 创建一个MVC请求中使用的视图对象
func NewView(c *Controller) *View {
// 视图目录路径查找优先级配置文件参数viewpath、启动参数viewpath、当前程序运行目录
path := gconsole.Option.Get("viewpath")
if path == "" {
path = gfile.SelfDir()
}
if config := ginstance.Config(); config != nil {
if r := config.Get("viewpath"); r != nil {
path = r.(string)
}
}
return &View{
ctl : c,
view : gview.GetView(path),
view : ginstance.View(),
data : make(map[string]interface{}),
}
}
@ -52,29 +40,33 @@ func (view *View) Assign(key string, value interface{}) {
view.data[key] = value
}
// 解析模板,并返回解析后的内容
func (view *View) Parse(file string) ([]byte, error) {
// 查询模板
tpl, err := view.view.Template(file)
if err != nil {
return nil, err
}
// 绑定函数
tpl.BindFunc("include", view.funcInclude)
// 执行解析
view.mu.RLock()
content, err := tpl.Parse(view.data)
view.mu.RUnlock()
return content, err
}
// 解析指定模板
func (view *View) Display(files...string) error {
file := "default"
if len(files) > 0 {
file = files[0]
}
// 查询模板
tpl, err := view.view.Template(file)
if err != nil {
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
return err
}
// 绑定函数
tpl.BindFunc("include", view.funcInclude)
// 执行解析
view.mu.RLock()
defer view.mu.RUnlock()
content, err := tpl.Parse(view.data)
if err != nil {
if content, err := view.Parse(file); err != nil {
view.ctl.Response.WriteString("Tpl Parsing Error: " + err.Error())
return err
} else {
view.ctl.Response.WriteString(content)
view.ctl.Response.Write(content)
}
return nil
}

View File

@ -13,6 +13,7 @@ import (
"path/filepath"
"gitee.com/johng/gf/g/util/gutil"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/net/grouter"
)
const (
@ -30,6 +31,7 @@ type Server struct {
status int8 // 当前服务器状态(0未启动1运行中)
handlerMap HandlerMap // 所有注册的回调函数
methodsMap map[string]bool // 所有支持的HTTP Method
Router *grouter.Router // 路由管理对象
}
// 域名、URI与回调函数的绑定记录表

View File

@ -23,6 +23,13 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) {
// 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置;
// 最后,如果以上都没有找到处理接口,那么进行文件处理;
func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
// 路由解析
uri := r.URL.String()
result, err := s.Router.Dispatch(uri)
if err == nil && strings.Compare(uri, result) != 0 {
r.URL, _ = r.URL.Parse(result)
}
// 构造请求/返回参数对象
request := &ClientRequest{}
response := &ServerResponse{}
request.Request = *r
@ -47,8 +54,14 @@ func (s *Server)callHandler(h *HandlerItem, r *ClientRequest, w *ServerResponse)
c.MethodByName("Shut").Call(nil)
} else {
h.faddr(s, r, w)
w.Output()
}
// 路由规则打包
if buffer, err := s.Router.Patch(w.Buffer()); err == nil {
w.ClearBuffer()
w.Write(buffer)
}
// 输出缓冲区
w.OutputBuffer()
}
// 处理静态文件请求
@ -59,7 +72,7 @@ func (s *Server)serveFile(w http.ResponseWriter, r *http.Request) {
path := strings.TrimRight(s.config.ServerRoot, string(filepath.Separator))
path = path + uri
path = gfile.RealPath(path)
if (path != "") {
if path != "" {
s.doServeFile(w, r, path)
} else {
s.NotFound(w, r)

View File

@ -59,8 +59,15 @@ func (r *ServerResponse) Buffer() []byte {
return r.buffer
}
// 清空缓冲区内容
func (r *ServerResponse) ClearBuffer() {
r.bufmu.Lock()
defer r.bufmu.Unlock()
r.buffer = make([]byte, 0)
}
// 输出缓冲区数据到客户端
func (r *ServerResponse) Output() {
func (r *ServerResponse) OutputBuffer() {
r.bufmu.Lock()
defer r.bufmu.Unlock()
if len(r.buffer) > 0 {

122
g/net/grouter/grouter.go Normal file
View File

@ -0,0 +1,122 @@
package grouter
import (
"sync"
"sort"
"bytes"
"errors"
"strings"
"gitee.com/johng/gf/g/util/gregx"
"gitee.com/johng/gf/g/container/gmap"
)
// 路由管理对象
type Router struct {
dmu sync.RWMutex // 解析规则互斥锁
pmu sync.RWMutex // 打包规则互斥锁
dkeys []string // 解析规则排序键名
pkeys []string // 打包规则排序键名
drules *gmap.StringStringMap // 解析规则
prules *gmap.StringStringMap // 打包规则
}
func New() *Router {
return &Router{
drules : gmap.NewStringStringMap(),
prules : gmap.NewStringStringMap(),
}
}
// 设置解析规则,例如:静态分页
// `\/([\w\.\-]+)\/([\w\.\-]+)\/page\/([\d\.\-]+)[\/\?]*`, "/user/list/page/2"
func (r *Router) SetRule(rule, replace string) {
r.drules.Set(rule, replace)
r.updateDispatchKeys()
}
// 批量设置解析规则
func (r *Router) SetRules(rules map[string]string) {
r.drules.BatchSet(rules)
r.updateDispatchKeys()
}
// 删除解析规则
func (r *Router) RemoveRule(rule string) {
r.drules.Remove(rule)
r.updateDispatchKeys()
}
// 设置打包规则
func (r *Router) SetPatchRule(rule, replace string) {
r.prules.Set(rule, replace)
r.updatePatchKeys()
}
// 批量设置打包规则
func (r *Router) SetPatchRules(rules map[string]string) {
r.prules.BatchSet(rules)
r.updatePatchKeys()
}
// 删除打包规则
func (r *Router) RemovePatchRule(rule string) {
r.prules.Remove(rule)
r.updatePatchKeys()
}
func (r *Router) updateDispatchKeys() {
r.dmu.Lock()
defer r.dmu.Unlock()
r.dkeys = r.drules.Keys()
sort.Slice(r.dkeys, func(i, j int) bool { return len(r.dkeys[i]) > len(r.dkeys[j]) })
}
func (r *Router) updatePatchKeys() {
r.pmu.Lock()
defer r.pmu.Unlock()
r.pkeys = r.prules.Keys()
sort.Slice(r.pkeys, func(i, j int) bool { return len(r.pkeys[i]) > len(r.pkeys[j]) })
}
// 解析URI
func (r *Router) Dispatch(uri string) (string, error) {
r.dmu.RLock()
defer r.dmu.RUnlock()
if len(r.dkeys) == 0 {
return uri, errors.New("no dispatch rules found")
}
for _, rule := range r.dkeys {
if replace := r.drules.Get(rule); replace != "" {
result, err := gregx.ReplaceString(rule, uri, replace)
if err != nil {
return result, err
}
if len(uri) != len(result) || strings.Compare(result, uri) != 0 {
return result, nil
}
}
}
return uri, nil
}
// 打包内容
func (r *Router) Patch(content []byte) ([]byte, error) {
r.pmu.RLock()
defer r.pmu.RUnlock()
if len(r.pkeys) == 0 {
return content, errors.New("no patch rules found")
}
for _, rule := range r.pkeys {
if replace := r.prules.Get(rule); replace != "" {
result, err := gregx.Replace(rule, content, []byte(replace))
if err != nil {
return result, err
}
if len(content) != len(result) || bytes.Compare(result, content) != 0 {
return result, nil
}
}
}
return content, nil
}

View File

@ -110,21 +110,21 @@ func (t *Template) Assign(k string, v interface{}) {
// 返回解析后的模板内容可以额外指定模板变量如果没有可以传入nil
// 函数内部的底层template必须每次调用都新生成一个防止错误html/template: cannot Parse after Execute
func (t *Template) Parse(data map[string]interface{}) (string, error) {
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 "", err
return nil, err
} else {
m := t.data
for k, v := range data {
m[k] = v
}
if err := tpl.Execute(buffer, m); err != nil {
return "", err
return nil, err
}
}
return buffer.String(), nil
return buffer.Bytes(), nil
}

View File

@ -5,10 +5,29 @@ import (
)
// 正则表达式是否匹配
func IsMatch(val, pattern string) bool {
match, err := regexp.Match(pattern, []byte(val))
func IsMatch(pattern string, src []byte) bool {
match, err := regexp.Match(pattern, src)
if err != nil {
return false
}
return match
}
func IsMatchString(pattern string, src string) bool {
return IsMatch(pattern, []byte(src))
}
// 正则替换(全部替换)
func Replace(pattern string, src, replace []byte) ([]byte, error) {
reg, err := regexp.Compile(pattern)
if err != nil {
return src, err
}
return reg.ReplaceAll(src, replace), nil
}
// 正则替换(全部替换),字符串
func ReplaceString(pattern, src, replace string) (string, error) {
r, e := Replace(pattern, []byte(src), []byte(replace))
return string(r), e
}

View File

@ -3,6 +3,7 @@ package user
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/frame/gmvc"
"gitee.com/johng/gf/g/frame/ginstance"
)
// 定义业务相关的控制器对象
@ -10,6 +11,7 @@ type ControllerUser struct {
gmvc.Controller
}
// 测试绑定对象
type T struct {
}
@ -28,8 +30,8 @@ func (t *T) Get(s *ghttp.Server, r *ghttp.ClientRequest, w *ghttp.ServerResponse
func init() {
//ghttp.GetServer("johng").Domain("localhost").BindHandler("/user", u.Info)
//ghttp.GetServer("johng").BindHandler("/test", Test)
ghttp.GetServer("johng").BindObjectRest("/test", &T{})
ghttp.GetServer("johng").BindController("/user", &ControllerUser{})
ginstance.Server().BindObjectRest("/test", &T{})
ginstance.Server().BindController("/user", &ControllerUser{})
}
// 定义操作逻辑

View File

@ -1,11 +1,11 @@
package main
import (
"gitee.com/johng/gf/g/net/ghttp"
"gitee.com/johng/gf/g/frame/ginstance"
_ "gitee.com/johng/gf/geg/frame/mvc/controller/user"
)
func main() {
ghttp.GetServer("johng").SetPort(8199)
ghttp.GetServer("johng").Run()
ginstance.Server().SetPort(8199)
ginstance.Server().Run()
}

View File

@ -1,11 +1,12 @@
package main
import (
"gitee.com/johng/gf/g/util/gregx"
"fmt"
"math"
)
func main() {
fmt.Printf("given id not match current maxid [%d]", int(math.MaxInt64))
s, _ := gregx.Replace(`\w`, []byte("/user/list/page/2"), []byte("-"))
fmt.Println(string(s))
}