mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
完成路由管理器
This commit is contained in:
@ -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邮件管理
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -40,7 +40,6 @@ func (c *Controller) Shut() {
|
||||
c.Cookie.Set(gDEFAULT_SESSION_ID_NAME, c.Session.Id())
|
||||
}
|
||||
c.Cookie.Output()
|
||||
c.Response.Output()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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与回调函数的绑定记录表
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
122
g/net/grouter/grouter.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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{})
|
||||
}
|
||||
|
||||
// 定义操作逻辑
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
Reference in New Issue
Block a user