From 878305fee32fcc2b4350b7ef24b9e0edf145be70 Mon Sep 17 00:00:00 2001 From: John Date: Sat, 3 Aug 2019 17:14:54 +0800 Subject: [PATCH] add middleware feature for ghttp.Server --- container/glist/glist.go | 6 ++ net/ghttp/ghttp_request.go | 43 ++++---- net/ghttp/ghttp_request_hook.go | 23 +++++ net/ghttp/ghttp_request_middleware.go | 13 +-- net/ghttp/ghttp_server.go | 8 +- net/ghttp/ghttp_server_handler.go | 4 +- net/ghttp/ghttp_server_router.go | 46 ++++----- net/ghttp/ghttp_server_router_hook.go | 116 +--------------------- net/ghttp/ghttp_server_router_serve.go | 70 +++++++++---- net/ghttp/ghttp_unit_router_basic_test.go | 18 ++-- net/ghttp/ghttp_unit_router_hook_test.go | 2 - 11 files changed, 143 insertions(+), 206 deletions(-) create mode 100644 net/ghttp/ghttp_request_hook.go diff --git a/container/glist/glist.go b/container/glist/glist.go index f8dc0a2f9..a953c734d 100644 --- a/container/glist/glist.go +++ b/container/glist/glist.go @@ -10,6 +10,7 @@ package glist import ( "container/list" + "encoding/json" "github.com/gogf/gf/internal/rwmutex" ) @@ -371,3 +372,8 @@ func (l *List) IteratorDesc(f func(e *Element) bool) { } l.mu.RUnlock() } + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (l *List) MarshalJSON() ([]byte, error) { + return json.Marshal(l.FrontAll()) +} diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index fb5a5bd7e..d18b05f5d 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -21,27 +21,28 @@ import ( // 请求对象 type Request struct { *http.Request - Id int // 请求ID(当前Server对象唯一) - Server *Server // 请求关联的服务器对象 - Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) - Session *Session // 与当前请求绑定的Session对象(并发安全) - Response *Response // 对应请求的返回数据操作对象 - Router *Router // 匹配到的路由对象 - EnterTime int64 // 请求进入时间(微秒) - LeaveTime int64 // 请求完成时间(微秒) - MiddleWare *MiddleWare // 中间件功能调用对象 - handlers []*handlerParsedItem // 请求执行服务函数列表(包含中间件、路由函数、钩子函数) - handlerIndex int // 当前执行函数的索引号 - parsedGet bool // GET参数是否已经解析 - parsedPost bool // POST参数是否已经解析 - queryVars map[string][]string // GET参数 - routerVars map[string][]string // 路由解析参数 - exit bool // 是否退出当前请求流程执行 - params map[string]interface{} // 开发者自定义参数(请求流程中有效) - parsedHost string // 解析过后不带端口号的服务器域名名称 - clientIp string // 解析过后的客户端IP地址 - rawContent []byte // 客户端提交的原始参数 - isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求) + Id int // 请求ID(当前Server对象唯一) + Server *Server // 请求关联的服务器对象 + Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) + Session *Session // 与当前请求绑定的Session对象(并发安全) + Response *Response // 对应请求的返回数据操作对象 + Router *Router // 匹配到的路由对象 + EnterTime int64 // 请求进入时间(微秒) + LeaveTime int64 // 请求完成时间(微秒) + MiddleWare *MiddleWare // 中间件功能调用对象 + handlers []*handlerParsedItem // 请求执行服务函数列表(包含中间件、路由函数、钩子函数) + handlerIndex int // 当前执行函数的索引号 + hasHookHandler bool // 是否注册有钩子函数(用于请求时提高钩子函数功能启用判断效率) + parsedGet bool // GET参数是否已经解析 + parsedPost bool // POST参数是否已经解析 + queryVars map[string][]string // GET参数 + routerVars map[string][]string // 路由解析参数 + exit bool // 是否退出当前请求流程执行 + params map[string]interface{} // 开发者自定义参数(请求流程中有效) + parsedHost string // 解析过后不带端口号的服务器域名名称 + clientIp string // 解析过后的客户端IP地址 + rawContent []byte // 客户端提交的原始参数 + isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求) } // 创建一个Request对象 diff --git a/net/ghttp/ghttp_request_hook.go b/net/ghttp/ghttp_request_hook.go new file mode 100644 index 000000000..138a802a5 --- /dev/null +++ b/net/ghttp/ghttp_request_hook.go @@ -0,0 +1,23 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package ghttp + +// 获得当前请求,指定类型的的钩子函数列表 +func (r *Request) getHookHandlers(hook string) []*handlerParsedItem { + if !r.hasHookHandler { + return nil + } + parsedItems := make([]*handlerParsedItem, 0, 4) + for _, v := range r.handlers { + if v.handler.hookName != hook { + continue + } + item := v + parsedItems = append(parsedItems, item) + } + return parsedItems +} diff --git a/net/ghttp/ghttp_request_middleware.go b/net/ghttp/ghttp_request_middleware.go index 130e8436e..ef64146a0 100644 --- a/net/ghttp/ghttp_request_middleware.go +++ b/net/ghttp/ghttp_request_middleware.go @@ -16,12 +16,13 @@ type MiddleWare struct { // 执行下一个请求流程处理函数 func (m *MiddleWare) Next() { item := (*handlerParsedItem)(nil) - for ; m.request.handlerIndex < len(m.request.handlers); m.request.handlerIndex++ { + for { // 是否停止请求执行 - if m.request.IsExited() { + if m.request.IsExited() || m.request.handlerIndex >= len(m.request.handlers) { return } item = m.request.handlers[m.request.handlerIndex] + m.request.handlerIndex++ // 通过中间件模式不执行钩子函数 if item.handler.itemType == gHANDLER_TYPE_HOOK { continue @@ -38,12 +39,12 @@ func (m *MiddleWare) Next() { niceCallFunc(func() { c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(m.request)}) }) - if m.request.IsExited() { + if !m.request.IsExited() { niceCallFunc(func() { c.MethodByName(item.handler.ctrlInfo.name).Call(nil) }) } - if m.request.IsExited() { + if !m.request.IsExited() { niceCallFunc(func() { c.MethodByName("Shut").Call(nil) }) @@ -54,12 +55,12 @@ func (m *MiddleWare) Next() { item.handler.initFunc(m.request) }) } - if m.request.IsExited() { + if !m.request.IsExited() { niceCallFunc(func() { item.handler.itemFunc(m.request) }) } - if m.request.IsExited() && item.handler.shutFunc != nil { + if !m.request.IsExited() && item.handler.shutFunc != nil { niceCallFunc(func() { item.handler.shutFunc(m.request) }) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index bf05558f4..5122edfda 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -42,10 +42,7 @@ type ( closeChan chan struct{} // 用以关闭事件通知的通道 servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配) - hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配) - middlewareTree map[string]interface{} // 所有注册的中间件(路由表,树型结构,哈希表+链表优先级匹配) serveCache *gcache.Cache // 服务注册路由内存缓存 - hooksCache *gcache.Cache // 事件回调路由内存缓存 routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断) statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) sessions *gcache.Cache // Session内存缓存 @@ -65,11 +62,12 @@ type ( // 服务函数注册信息 handlerItem struct { itemName string // 注册的函数名称信息(用于路由信息打印) - itemType int // 注册函数类型(对象/函数/控制器/中间件) + itemType int // 注册函数类型(对象/函数/控制器/中间件/钩子函数) itemFunc HandlerFunc // 函数内存地址(与以上两个参数二选一) initFunc HandlerFunc // 初始化请求回调函数(对象注册方式下有效) shutFunc HandlerFunc // 完成请求回调函数(对象注册方式下有效) ctrlInfo *handlerController // 控制器服务函数反射信息 + hookName string // 钩子类型名称(注册函数类型为钩子函数下有效) router *Router // 注册时绑定的路由对象 } @@ -208,9 +206,7 @@ func GetServer(name ...interface{}) *Server { serverCount: gtype.NewInt(), statusHandlerMap: make(map[string]HandlerFunc), serveTree: make(map[string]interface{}), - hooksTree: make(map[string]interface{}), serveCache: gcache.New(), - hooksCache: gcache.New(), routesMap: make(map[string][]registeredRouteItem), sessions: gcache.New(), servedCount: gtype.NewInt(), diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index f898e20d8..c2ff1d9c6 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -97,7 +97,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { // 动态服务检索 if !request.isFileRequest || isStaticDir { - request.handlers = s.getHandlersWithCache(request) + request.handlers, request.hasHookHandler = s.getHandlersWithCache(request) } // 判断最终对该请求提供的服务方式 @@ -114,7 +114,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { // 静态服务 s.serveFile(request, staticFile) } else { - if request.handlers != nil { + if len(request.handlers) > 0 { // 动态服务 request.MiddleWare.Next() } else { diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 38b4e649a..2412cb2c4 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -7,12 +7,13 @@ package ghttp import ( - "container/list" "errors" "fmt" "runtime" "strings" + "github.com/gogf/gf/container/glist" + "github.com/gogf/gf/os/glog" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" @@ -101,16 +102,8 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string if _, ok := s.serveTree[domain]; !ok { s.serveTree[domain] = make(map[string]interface{}) } - // 用于遍历的指针 - p := s.serveTree[domain] - if len(hookName) > 0 { - if _, ok := p.(map[string]interface{})[hookName]; !ok { - p.(map[string]interface{})[hookName] = make(map[string]interface{}) - } - p = p.(map[string]interface{})[hookName] - } // 当前节点的规则链表 - lists := make([]*list.List, 0) + lists := make([]*glist.List, 0) array := ([]string)(nil) if strings.EqualFold("/", uri) { array = []string{"/"} @@ -118,7 +111,8 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string array = strings.Split(uri[1:], "/") } // 键名"*fuzz"代表模糊匹配节点,其下会有一个链表; - // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性; + // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性,优先级越高越排前; + p := s.serveTree[domain] for k, v := range array { if len(v) == 0 { continue @@ -129,10 +123,10 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string // 由于是模糊规则,因此这里会有一个*list,用以将后续的路由规则加进来, // 检索会从叶子节点的链表往根节点按照优先级进行检索 if v, ok := p.(map[string]interface{})["*list"]; !ok { - p.(map[string]interface{})["*list"] = list.New() - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + p.(map[string]interface{})["*list"] = glist.New() + lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List)) } else { - lists = append(lists, v.(*list.List)) + lists = append(lists, v.(*glist.List)) } } // 属性层级数据写入 @@ -143,10 +137,10 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string // 到达叶子节点,往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表) if k == len(array)-1 && v != "*fuzz" { if v, ok := p.(map[string]interface{})["*list"]; !ok { - p.(map[string]interface{})["*list"] = list.New() - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + p.(map[string]interface{})["*list"] = glist.New() + lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List)) } else { - lists = append(lists, v.(*list.List)) + lists = append(lists, v.(*glist.List)) } } } @@ -160,15 +154,14 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string switch handler.itemType { // 判断是否已存在相同的路由注册项,如果是普通路由注册则进行替换 case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: - if handler.itemType == gHANDLER_TYPE_HANDLER { - if strings.EqualFold(handler.router.Domain, item.router.Domain) && - strings.EqualFold(handler.router.Method, item.router.Method) && - strings.EqualFold(handler.router.Uri, item.router.Uri) { - e.Value = handler - pushed = true - break - } + if strings.EqualFold(handler.router.Domain, item.router.Domain) && + strings.EqualFold(handler.router.Method, item.router.Method) && + strings.EqualFold(handler.router.Uri, item.router.Uri) { + e.Value = handler + pushed = true + break } + fallthrough // 否则,那么判断优先级,决定插入顺序 default: @@ -183,8 +176,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string l.PushBack(handler) } } - // gutil.Dump(s.serveTree) - // gutil.Dump(s.hooksTree) + //gutil.Dump(s.serveTree) if _, ok := s.routesMap[regkey]; !ok { s.routesMap[regkey] = make([]registeredRouteItem, 0) } diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index a08e8a586..8a38ae629 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -7,12 +7,8 @@ package ghttp import ( - "container/list" "reflect" "runtime" - "strings" - - "github.com/gogf/gf/text/gregex" ) // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 @@ -21,6 +17,7 @@ func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFun itemType: gHANDLER_TYPE_HOOK, itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(), itemFunc: handler, + hookName: hook, }, hook) } @@ -34,11 +31,7 @@ func (s *Server) BindHookHandlerByMap(pattern string, hookmap map[string]Handler // 事件回调处理,内部使用了缓存处理. // 并按照指定hook回调函数的优先级及注册顺序进行调用 func (s *Server) callHookHandler(hook string, r *Request) { - // 如果没有hook注册,那么不用执行后续逻辑 - if len(s.hooksTree) == 0 { - return - } - hookItems := s.getHookHandlerWithCache(hook, r) + hookItems := r.getHookHandlers(hook) if len(hookItems) > 0 { // 备份原有的router变量 oldRouterVars := r.routerVars @@ -86,111 +79,6 @@ func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{} return } -// 查询请求处理方法, 带缓存机制,按照Host、Method、Path进行缓存. -func (s *Server) getHookHandlerWithCache(hook string, r *Request) []*handlerParsedItem { - cacheItems := ([]*handlerParsedItem)(nil) - cacheKey := s.handlerKey(hook, r.Method, r.URL.Path, r.GetHost()) - if v := s.hooksCache.Get(cacheKey); v == nil { - cacheItems = s.searchHookHandler(r.Method, r.URL.Path, r.GetHost(), hook) - if cacheItems != nil { - s.hooksCache.Set(cacheKey, cacheItems, s.config.RouterCacheExpire*1000) - } - } else { - cacheItems = v.([]*handlerParsedItem) - } - return cacheItems -} - -// 事件方法检索 -func (s *Server) searchHookHandler(method, path, domain, hook string) []*handlerParsedItem { - if len(path) == 0 { - return nil - } - // 遍历检索的域名列表 - domains := []string{gDEFAULT_DOMAIN} - if !strings.EqualFold(gDEFAULT_DOMAIN, domain) { - domains = append(domains, domain) - } - // URL.Path层级拆分 - array := ([]string)(nil) - if strings.EqualFold("/", path) { - array = []string{"/"} - } else { - array = strings.Split(path[1:], "/") - } - parsedItems := make([]*handlerParsedItem, 0, 8) - for _, domain := range domains { - p, ok := s.hooksTree[domain] - if !ok { - continue - } - p, ok = p.(map[string]interface{})[hook] - if !ok { - continue - } - // 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理 - lists := make([]*list.List, 0) - for k, v := range array { - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } - if _, ok := p.(map[string]interface{})[v]; ok { - p = p.(map[string]interface{})[v] - if k == len(array)-1 { - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - break - } - } - } else { - if _, ok := p.(map[string]interface{})["*fuzz"]; ok { - p = p.(map[string]interface{})["*fuzz"] - } - } - // 如果是叶子节点,同时判断当前层级的"*fuzz"键名,解决例如:/user/*action 匹配 /user 的规则 - if k == len(array)-1 { - if _, ok := p.(map[string]interface{})["*fuzz"]; ok { - p = p.(map[string]interface{})["*fuzz"] - } - if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } - } - } - - // 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高 - for i := len(lists) - 1; i >= 0; i-- { - for e := lists[i].Front(); e != nil; e = e.Next() { - handler := e.Value.(*handlerItem) - // 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储 - if strings.EqualFold(handler.router.Method, gDEFAULT_METHOD) || strings.EqualFold(handler.router.Method, method) { - // 注意当不带任何动态路由规则时,len(match) == 1 - if match, err := gregex.MatchString(handler.router.RegRule, path); err == nil && len(match) > 0 { - parsedItem := &handlerParsedItem{handler, nil} - // 如果需要query匹配,那么需要重新正则解析URL - if len(handler.router.RegNames) > 0 { - if len(match) > len(handler.router.RegNames) { - parsedItem.values = make(map[string][]string) - // 如果存在存在同名路由参数名称,那么执行数组追加 - for i, name := range handler.router.RegNames { - if _, ok := parsedItem.values[name]; ok { - parsedItem.values[name] = append(parsedItem.values[name], match[i+1]) - } else { - parsedItem.values[name] = []string{match[i+1]} - } - } - } - } - parsedItems = append(parsedItems, parsedItem) - } - } - } - } - return parsedItems - } - return nil -} - // 生成hook key,如果是hook key,那么使用'%'符号分隔 func (s *Server) handlerKey(hook, method, path, domain string) string { return hook + "%" + s.serveHandlerKey(method, path, domain) diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index a582636ce..c72473336 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -7,35 +7,40 @@ package ghttp import ( - "container/list" + "encoding/json" + "fmt" "strings" + "github.com/gogf/gf/container/glist" "github.com/gogf/gf/text/gregex" ) +// 缓存数据项 +type handlerCacheItem struct { + parsedItems []*handlerParsedItem + hasHook bool +} + // 查询请求处理方法. // 内部带锁机制,可以并发读,但是不能并发写;并且有缓存机制,按照Host、Method、Path进行缓存. -func (s *Server) getHandlersWithCache(r *Request) []*handlerParsedItem { +func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*handlerParsedItem, hasHook bool) { cacheKey := s.serveHandlerKey(r.Method, r.URL.Path, r.GetHost()) - cacheItems := ([]*handlerParsedItem)(nil) if v := s.serveCache.Get(cacheKey); v == nil { - cacheItems = s.searchHandlers(r.Method, r.URL.Path, r.GetHost()) - if cacheItems != nil { - s.serveCache.Set(cacheKey, cacheItems, s.config.RouterCacheExpire*1000) + parsedItems, hasHook = s.searchHandlers(r.Method, r.URL.Path, r.GetHost()) + if parsedItems != nil { + s.serveCache.Set(cacheKey, &handlerCacheItem{parsedItems, hasHook}, s.config.RouterCacheExpire*1000) } } else { - cacheItems = v.([]*handlerParsedItem) + item := v.(*handlerCacheItem) + return item.parsedItems, item.hasHook } - if len(cacheItems) == 0 { - return nil - } - return cacheItems + return } // 路由注册方法检索,返回所有该路由的注册函数,构造成数组返回 -func (s *Server) searchHandlers(method, path, domain string) []*handlerParsedItem { +func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*handlerParsedItem, hasHook bool) { if len(path) == 0 { - return nil + return nil, false } // 遍历检索的域名列表,优先遍历默认域名 domains := []string{gDEFAULT_DOMAIN} @@ -49,24 +54,25 @@ func (s *Server) searchHandlers(method, path, domain string) []*handlerParsedIte } else { array = strings.Split(path[1:], "/") } - parsedItems := make([]*handlerParsedItem, 0, 16) + parsedItems = make([]*handlerParsedItem, 0, 16) isServeHandlerAdded := false for _, domain := range domains { p, ok := s.serveTree[domain] if !ok { continue } + //gutil.Dump(p) // 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理 - lists := make([]*list.List, 0, 16) + lists := make([]*glist.List, 0, 16) for k, v := range array { if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List)) } if _, ok := p.(map[string]interface{})[v]; ok { p = p.(map[string]interface{})[v] if k == len(array)-1 { if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List)) break } } @@ -81,7 +87,7 @@ func (s *Server) searchHandlers(method, path, domain string) []*handlerParsedIte p = p.(map[string]interface{})["*fuzz"] } if _, ok := p.(map[string]interface{})["*list"]; ok { - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List)) } } } @@ -124,12 +130,38 @@ func (s *Server) searchHandlers(method, path, domain string) []*handlerParsedIte isServeHandlerAdded = true } } + if item.itemType == gHANDLER_TYPE_HOOK { + hasHook = true + } } } } } } - return parsedItems + return +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (item *handlerItem) MarshalJSON() ([]byte, error) { + if item.hookName != "" { + return json.Marshal( + fmt.Sprintf( + `%s %s:%s (%s)`, + item.router.Uri, + item.router.Domain, + item.router.Method, + item.hookName, + ), + ) + } + return json.Marshal( + fmt.Sprintf( + `%s %s:%s`, + item.router.Uri, + item.router.Domain, + item.router.Method, + ), + ) } // 生成回调方法查询的Key diff --git a/net/ghttp/ghttp_unit_router_basic_test.go b/net/ghttp/ghttp_unit_router_basic_test.go index 84a29e586..34b4563ae 100644 --- a/net/ghttp/ghttp_unit_router_basic_test.go +++ b/net/ghttp/ghttp_unit_router_basic_test.go @@ -23,18 +23,18 @@ func Test_Router_Basic(t *testing.T) { s.BindHandler("/:name", func(r *ghttp.Request) { r.Response.Write("/:name") }) - //s.BindHandler("/:name/update", func(r *ghttp.Request) { - // r.Response.Write(r.Get("name")) - //}) - //s.BindHandler("/:name/:action", func(r *ghttp.Request) { - // r.Response.Write(r.Get("action")) - //}) + s.BindHandler("/:name/update", func(r *ghttp.Request) { + r.Response.Write(r.Get("name")) + }) + s.BindHandler("/:name/:action", func(r *ghttp.Request) { + r.Response.Write(r.Get("action")) + }) s.BindHandler("/:name/*any", func(r *ghttp.Request) { r.Response.Write(r.Get("any")) }) - //s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request) { - // r.Response.Write(r.Get("field")) - //}) + s.BindHandler("/user/list/{field}.html", func(r *ghttp.Request) { + r.Response.Write(r.Get("field")) + }) s.SetPort(p) s.SetDumpRouteMap(false) s.Start() diff --git a/net/ghttp/ghttp_unit_router_hook_test.go b/net/ghttp/ghttp_unit_router_hook_test.go index 786856ab2..428d78696 100644 --- a/net/ghttp/ghttp_unit_router_hook_test.go +++ b/net/ghttp/ghttp_unit_router_hook_test.go @@ -24,8 +24,6 @@ func Test_Router_Hook_Basic(t *testing.T) { "AfterServe": func(r *ghttp.Request) { r.Response.Write("2") }, "BeforeOutput": func(r *ghttp.Request) { r.Response.Write("3") }, "AfterOutput": func(r *ghttp.Request) { r.Response.Write("4") }, - "BeforeClose": func(r *ghttp.Request) { r.Response.Write("5") }, - "AfterClose": func(r *ghttp.Request) { r.Response.Write("6") }, }) s.BindHandler("/test/test", func(r *ghttp.Request) { r.Response.Write("test")