mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
Web Server增加启动时的路由展示特性,开发中
This commit is contained in:
@ -27,6 +27,7 @@ import (
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"time"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -52,26 +53,26 @@ const (
|
||||
// ghttp.Server结构体
|
||||
type Server struct {
|
||||
// 基本属性变量
|
||||
name string // 服务名称,方便识别
|
||||
paths *gspath.SPath // 静态文件检索对象(类似nginx tryfile功能)
|
||||
config ServerConfig // 配置对象
|
||||
servers []*gracefulServer // 底层http.Server列表
|
||||
methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充)
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
name string // 服务名称,方便识别
|
||||
paths *gspath.SPath // 静态文件检索对象(类似nginx tryfile功能)
|
||||
config ServerConfig // 配置对象
|
||||
servers []*gracefulServer // 底层http.Server列表
|
||||
methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充)
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
// 服务注册相关
|
||||
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
||||
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
||||
serveCache *gcache.Cache // 服务注册路由内存缓存
|
||||
hooksCache *gcache.Cache // 事件回调路由内存缓存
|
||||
routesMap map[string]string // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
|
||||
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
||||
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
||||
serveCache *gcache.Cache // 服务注册路由内存缓存
|
||||
hooksCache *gcache.Cache // 事件回调路由内存缓存
|
||||
routesMap map[string]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
|
||||
// 自定义状态码回调
|
||||
hsmu sync.RWMutex // status handler互斥锁
|
||||
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
||||
hsmu sync.RWMutex // status handler互斥锁
|
||||
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
||||
// SESSION
|
||||
sessions *gcache.Cache // Session内存缓存
|
||||
sessions *gcache.Cache // Session内存缓存
|
||||
// Logger
|
||||
logger *glog.Logger // 日志管理对象
|
||||
logger *glog.Logger // 日志管理对象
|
||||
}
|
||||
|
||||
// 路由对象
|
||||
@ -89,7 +90,8 @@ type handlerMap map[string]*handlerItem
|
||||
|
||||
// http回调函数注册信息
|
||||
type handlerItem struct {
|
||||
rtype int // 注册方式
|
||||
name string // 注册的方法名称信息
|
||||
rtype int // 注册方式(执行对象/回调函数/控制器)
|
||||
ctype reflect.Type // 控制器类型(反射类型)
|
||||
fname string // 回调方法名称
|
||||
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
|
||||
@ -104,6 +106,12 @@ type handlerParsedItem struct {
|
||||
values map[string][]string // 特定URL.Path的Router解析参数
|
||||
}
|
||||
|
||||
// 已注册的路由项
|
||||
type registeredRouteItem struct {
|
||||
file string // 文件路径及行数地址
|
||||
handler *handlerItem // 路由注册项
|
||||
}
|
||||
|
||||
// HTTP注册函数
|
||||
type HandlerFunc func(r *Request)
|
||||
|
||||
@ -169,7 +177,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
hooksTree : make(map[string]interface{}),
|
||||
serveCache : gcache.New(),
|
||||
hooksCache : gcache.New(),
|
||||
routesMap : make(map[string]string),
|
||||
routesMap : make(map[string]registeredRouteItem),
|
||||
sessions : gcache.New(),
|
||||
servedCount : gtype.NewInt(),
|
||||
closeQueue : gqueue.New(),
|
||||
@ -251,9 +259,28 @@ func (s *Server) Start() error {
|
||||
|
||||
// 开启异步关闭队列处理循环
|
||||
s.startCloseQueueLoop()
|
||||
|
||||
// 打印展示路由表
|
||||
s.DumpRoutesMap()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 打印展示路由表
|
||||
func (s *Server) DumpRoutesMap() {
|
||||
for _, v := range s.routesMap {
|
||||
fmt.Println(v.handler.name)
|
||||
//switch v.handler.rtype {
|
||||
// case gROUTE_REGISTER_HANDLER:
|
||||
// fmt.Println(v.handler.name)
|
||||
// case gROUTE_REGISTER_OBJECT:
|
||||
// //fmt.Println(v.handler.name)
|
||||
// case gROUTE_REGISTER_CONTROLLER:
|
||||
// fmt.Println(v.handler.name)
|
||||
//
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
// 阻塞执行监听
|
||||
func (s *Server) Run() error {
|
||||
if err := s.Start(); err != nil {
|
||||
|
||||
@ -78,7 +78,10 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
|
||||
} else {
|
||||
defer func() {
|
||||
if resultErr == nil {
|
||||
s.routesMap[regkey] = caller
|
||||
s.routesMap[regkey] = registeredRouteItem{
|
||||
file : caller,
|
||||
handler : handler,
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@ -177,8 +177,8 @@ func (s *Server) searchHookHandler(method, path, domain, hook string) []*handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// 生成hook key
|
||||
// 生成hook key,如果是hook key,那么使用'%'符号分隔
|
||||
func (s *Server) hookHandlerKey(hook, method, path, domain string) string {
|
||||
return hook + "@" + s.serveHandlerKey(method, path, domain)
|
||||
return hook + "%" + s.serveHandlerKey(method, path, domain)
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,9 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
"gitee.com/johng/gf/g/util/gstr"
|
||||
)
|
||||
|
||||
// 绑定控制器,控制器需要实现gmvc.Controller接口
|
||||
@ -25,10 +28,12 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
}
|
||||
}
|
||||
// 遍历控制器,获取方法列表,并构造成uri
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
t := v.Type()
|
||||
sname := t.Elem().Name()
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
t := v.Type()
|
||||
sname := t.Elem().Name()
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
mname := t.Method(i).Name
|
||||
if methodMap != nil && !methodMap[mname] {
|
||||
@ -37,8 +42,13 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
if mname == "Init" || mname == "Shut" || mname == "Exit" {
|
||||
continue
|
||||
}
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
rtype : gROUTE_REGISTER_CONTROLLER,
|
||||
ctype : v.Elem().Type(),
|
||||
fname : mname,
|
||||
@ -54,6 +64,7 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
}
|
||||
}
|
||||
m[p] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
rtype : gROUTE_REGISTER_CONTROLLER,
|
||||
ctype : v.Elem().Type(),
|
||||
fname : mname,
|
||||
@ -68,15 +79,21 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
|
||||
func (s *Server)BindControllerMethod(pattern string, c Controller, method string) error {
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
e := v.Type().Elem()
|
||||
t := v.Elem().Type()
|
||||
sname := e.Name()
|
||||
t := v.Type()
|
||||
sname := t.Elem().Name()
|
||||
mname := strings.TrimSpace(method)
|
||||
if !v.MethodByName(mname).IsValid() {
|
||||
return errors.New("invalid method name:" + mname)
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
|
||||
m[key] = &handlerItem {
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
rtype : gROUTE_REGISTER_CONTROLLER,
|
||||
ctype : t,
|
||||
fname : mname,
|
||||
@ -91,21 +108,28 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
|
||||
// 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话
|
||||
func (s *Server)BindControllerRest(pattern string, c Controller) error {
|
||||
// 遍历控制器,获取方法列表,并构造成uri
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
t := v.Type()
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(c)
|
||||
t := v.Type()
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
// 如果存在与HttpMethod对应名字的方法,那么绑定这些方法
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
name := t.Method(i).Name
|
||||
method := strings.ToUpper(name)
|
||||
mname := t.Method(i).Name
|
||||
method := strings.ToUpper(mname)
|
||||
if _, ok := s.methodsMap[method]; !ok {
|
||||
continue
|
||||
}
|
||||
key := name + ":" + pattern
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if ctlName[0] == '*' {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
key := mname + ":" + pattern
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
|
||||
rtype : gROUTE_REGISTER_CONTROLLER,
|
||||
ctype : v.Elem().Type(),
|
||||
fname : name,
|
||||
fname : mname,
|
||||
faddr : nil,
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,11 +12,14 @@ import (
|
||||
"strings"
|
||||
"gitee.com/johng/gf/g/util/gstr"
|
||||
"bytes"
|
||||
"runtime"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
|
||||
func (s *Server) BindHandler(pattern string, handler HandlerFunc) error {
|
||||
return s.bindHandlerItem(pattern, &handlerItem {
|
||||
name : runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
|
||||
rtype : gROUTE_REGISTER_HANDLER,
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
|
||||
@ -11,6 +11,9 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"reflect"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/util/gstr"
|
||||
"gitee.com/johng/gf/g/os/gfile"
|
||||
)
|
||||
|
||||
// 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面
|
||||
@ -23,9 +26,9 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
methodMap[strings.TrimSpace(v)] = true
|
||||
}
|
||||
}
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(obj)
|
||||
t := v.Type()
|
||||
m := make(handlerMap)
|
||||
v := reflect.ValueOf(obj)
|
||||
t := v.Type()
|
||||
sname := t.Elem().Name()
|
||||
finit := (func(*Request))(nil)
|
||||
fshut := (func(*Request))(nil)
|
||||
@ -35,6 +38,8 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
if v.MethodByName("Shut").IsValid() {
|
||||
fshut = v.MethodByName("Shut").Interface().(func(*Request))
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
mname := t.Method(i).Name
|
||||
if methodMap != nil && !methodMap[mname] {
|
||||
@ -43,8 +48,13 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
if mname == "Init" || mname == "Shut" {
|
||||
continue
|
||||
}
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
rtype : gROUTE_REGISTER_OBJECT,
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
@ -62,6 +72,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
|
||||
}
|
||||
}
|
||||
m[p] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
rtype : gROUTE_REGISTER_OBJECT,
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
@ -94,8 +105,15 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
if v.MethodByName("Shut").IsValid() {
|
||||
fshut = v.MethodByName("Shut").Interface().(func(*Request))
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
|
||||
m[key] = &handlerItem{
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
rtype : gROUTE_REGISTER_OBJECT,
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
@ -121,14 +139,21 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
|
||||
if v.MethodByName("Shut").IsValid() {
|
||||
fshut = v.MethodByName("Shut").Interface().(func(*Request))
|
||||
}
|
||||
pkgPath := t.Elem().PkgPath()
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
name := t.Method(i).Name
|
||||
method := strings.ToUpper(name)
|
||||
mname := t.Method(i).Name
|
||||
method := strings.ToUpper(mname)
|
||||
if _, ok := s.methodsMap[method]; !ok {
|
||||
continue
|
||||
}
|
||||
key := name + ":" + pattern
|
||||
pkgName := gfile.Basename(pkgPath)
|
||||
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
|
||||
if objName[0] == '*' {
|
||||
objName = fmt.Sprintf(`(%s)`, objName)
|
||||
}
|
||||
key := mname + ":" + pattern
|
||||
m[key] = &handlerItem {
|
||||
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
|
||||
rtype : gROUTE_REGISTER_OBJECT,
|
||||
ctype : nil,
|
||||
fname : "",
|
||||
|
||||
@ -2,11 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/encoding/gurl"
|
||||
"gitee.com/johng/gf/g/encoding/ghtml"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(gurl.Decode("<"))
|
||||
fmt.Println(ghtml.SpecialChars("<"))
|
||||
type T struct {
|
||||
|
||||
}
|
||||
func (t *T) Test() {
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := new(T)
|
||||
fmt.Println(runtime.FuncForPC(reflect.ValueOf(t.Test).Pointer()).Name())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user