diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index 2412cb2c4..30dda0652 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -9,9 +9,10 @@ package ghttp import ( "errors" "fmt" - "runtime" "strings" + "github.com/gogf/gf/internal/debug" + "github.com/gogf/gf/container/glist" "github.com/gogf/gf/os/glog" @@ -19,6 +20,10 @@ import ( "github.com/gogf/gf/text/gstr" ) +const ( + gFILTER_KEY = "/gf/net/ghttp/ghttp" +) + // 解析pattern func (s *Server) parsePattern(pattern string) (domain, method, path string, err error) { path = strings.TrimSpace(pattern) @@ -46,31 +51,15 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err return } -// 获得服务注册的文件地址信息 -func (s *Server) getHandlerRegisterCallerLine(handler *handlerItem) string { - skip := 5 - if handler.itemType == gHANDLER_TYPE_HANDLER { - skip = 4 - } - if _, file, line, ok := runtime.Caller(skip); ok { - return fmt.Sprintf("%s:%d", file, line) - } - return "" -} - // 路由注册处理方法。 // 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点; // 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高; -func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string) { +func (s *Server) setHandler(pattern string, handler *handlerItem) { // Web Server正常运行时无法动态注册路由方法 if s.Status() == SERVER_STATUS_RUNNING { glog.Error("cannot bind handler while server running") return } - var hookName string - if len(hook) > 0 { - hookName = hook[0] - } domain, method, uri, err := s.parsePattern(pattern) if err != nil { glog.Error("invalid pattern:", pattern, err) @@ -81,15 +70,14 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string return } // 注册地址记录及重复注册判断 - regkey := s.handlerKey(hookName, method, uri, domain) - caller := s.getHandlerRegisterCallerLine(handler) - if len(hook) == 0 { - if item, ok := s.routesMap[regkey]; ok { + regKey := s.handlerKey(handler.hookName, method, uri, domain) + switch handler.itemType { + case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: + if item, ok := s.routesMap[regKey]; ok { glog.Errorf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file) return } } - // 注册的路由信息对象 handler.router = &Router{ Uri: uri, @@ -165,7 +153,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string // 否则,那么判断优先级,决定插入顺序 default: - if s.compareRouterPriority(handler.router, item.router) { + if s.compareRouterPriority(handler, item) { l.InsertBefore(handler, e) pushed = true break @@ -177,11 +165,12 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string } } //gutil.Dump(s.serveTree) - if _, ok := s.routesMap[regkey]; !ok { - s.routesMap[regkey] = make([]registeredRouteItem, 0) + if _, ok := s.routesMap[regKey]; !ok { + s.routesMap[regKey] = make([]registeredRouteItem, 0) } - s.routesMap[regkey] = append(s.routesMap[regkey], registeredRouteItem{ - file: caller, + file, line := debug.CallerWithFilter(gFILTER_KEY) + s.routesMap[regKey] = append(s.routesMap[regKey], registeredRouteItem{ + file: fmt.Sprintf(`%s:%d`, file, line), handler: handler, }) } @@ -189,14 +178,22 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ...string // 对比两个handlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序。 // 返回值true表示newRouter优先级比oldRouter高,会被添加链表中oldRouter的前面;否则后面。 // 优先级比较规则: -// 1、层级越深优先级越高(对比/数量); -// 2、模糊规则优先级:{xxx} > :xxx > *xxx; -func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { - // 优先比较层级,层级越深优先级越高 - if newRouter.Priority > oldRouter.Priority { +// 1、中间件优先级最高,按照添加顺序优先级执行; +// 2、其他路由注册类型,层级越深优先级越高(对比/数量); +// 3、模糊规则优先级:{xxx} > :xxx > *xxx; +func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerItem) bool { + // 中间件优先级最高,按照添加顺序优先级执行 + if newItem.itemType == gHANDLER_TYPE_MIDDLEWARE && oldItem.itemType == gHANDLER_TYPE_MIDDLEWARE { + return false + } + if newItem.itemType == gHANDLER_TYPE_MIDDLEWARE && oldItem.itemType != gHANDLER_TYPE_MIDDLEWARE { return true } - if newRouter.Priority < oldRouter.Priority { + // 优先比较层级,层级越深优先级越高 + if newItem.router.Priority > oldItem.router.Priority { + return true + } + if newItem.router.Priority < oldItem.router.Priority { return false } // 精准匹配比模糊匹配规则优先级高,例如:/name/act 比 /{name}/:act 优先级高 @@ -204,7 +201,7 @@ func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { var fuzzyCountNameNew, fuzzyCountNameOld int var fuzzyCountAnyNew, fuzzyCountAnyOld int var fuzzyCountTotalNew, fuzzyCountTotalOld int - for _, v := range newRouter.Uri { + for _, v := range newItem.router.Uri { switch v { case '{': fuzzyCountFieldNew++ @@ -214,7 +211,7 @@ func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { fuzzyCountAnyNew++ } } - for _, v := range oldRouter.Uri { + for _, v := range oldItem.router.Uri { switch v { case '{': fuzzyCountFieldOld++ @@ -253,10 +250,10 @@ func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { /* 模糊规则数量相等,后续不用再判断*规则的数量比较了 */ // 比较HTTP METHOD,更精准的优先级更高 - if newRouter.Method != gDEFAULT_METHOD { + if newItem.router.Method != gDEFAULT_METHOD { return true } - if oldRouter.Method != gDEFAULT_METHOD { + if oldItem.router.Method != gDEFAULT_METHOD { return true } diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index 8a38ae629..5b3825951 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -18,7 +18,7 @@ func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFun itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(), itemFunc: handler, hookName: hook, - }, hook) + }) } // 通过map批量绑定回调函数 diff --git a/net/ghttp/ghttp_server_router_middleware.go b/net/ghttp/ghttp_server_router_middleware.go index 065ca859f..372960948 100644 --- a/net/ghttp/ghttp_server_router_middleware.go +++ b/net/ghttp/ghttp_server_router_middleware.go @@ -5,3 +5,30 @@ // You can obtain one at https://github.com/gogf/gf. package ghttp + +import ( + "reflect" + "runtime" +) + +// 注册中间件,绑定到指定的路由规则上,中间件参数支持多个。 +func (s *Server) BindMiddleWare(pattern string, handlers ...HandlerFunc) { + for _, handler := range handlers { + s.setHandler(pattern, &handlerItem{ + itemType: gHANDLER_TYPE_MIDDLEWARE, + itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(), + itemFunc: handler, + }) + } +} + +// 注册中间件,绑定到全局路由规则("/*")上,中间件参数支持多个。 +func (s *Server) AddMiddleWare(handlers ...HandlerFunc) { + for _, handler := range handlers { + s.setHandler("/*", &handlerItem{ + itemType: gHANDLER_TYPE_MIDDLEWARE, + itemName: runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(), + itemFunc: handler, + }) + } +} diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index c72473336..61adba9d5 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -54,7 +54,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han } else { array = strings.Split(path[1:], "/") } - parsedItems = make([]*handlerParsedItem, 0, 16) + parsedItemList := glist.New() + lastMiddleWareItem := (*glist.Element)(nil) isServeHandlerAdded := false for _, domain := range domains { p, ok := s.serveTree[domain] @@ -122,22 +123,41 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*han } } } - parsedItems = append(parsedItems, parsedItem) + switch item.itemType { // 服务路由函数只能添加一次 - if !isServeHandlerAdded { - switch item.itemType { - case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: - isServeHandlerAdded = true + case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: + isServeHandlerAdded = true + parsedItemList.PushBack(parsedItem) + + // 中间件需要排序 + case gHANDLER_TYPE_MIDDLEWARE: + if lastMiddleWareItem == nil { + lastMiddleWareItem = parsedItemList.PushFront(parsedItem) + } else { + lastMiddleWareItem = parsedItemList.InsertAfter(parsedItem, lastMiddleWareItem) } - } - if item.itemType == gHANDLER_TYPE_HOOK { + + // 钩子函数存在性判断 + case gHANDLER_TYPE_HOOK: hasHook = true + parsedItemList.PushBack(parsedItem) + + default: + parsedItemList.PushBack(parsedItem) } } } } } } + if parsedItemList.Len() > 0 { + index := 0 + parsedItems = make([]*handlerParsedItem, parsedItemList.Len()) + for e := parsedItemList.Front(); e != nil; e = e.Next() { + parsedItems[index] = e.Value.(*handlerParsedItem) + index++ + } + } return } diff --git a/net/ghttp/ghttp_unit_router_middleware_test.go b/net/ghttp/ghttp_unit_router_middleware_test.go new file mode 100644 index 000000000..bba0961fb --- /dev/null +++ b/net/ghttp/ghttp_unit_router_middleware_test.go @@ -0,0 +1,44 @@ +// Copyright 2018 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_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/test/gtest" +) + +func Test_Router_MiddleWare_Basic1(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.BindHandler("/test/test", func(r *ghttp.Request) { + r.Response.Write("test") + }) + s.AddMiddleWare(func(r *ghttp.Request) { + r.Response.Write("1") + r.MiddleWare.Next() + r.Response.Write("2") + }) + s.SetPort(p) + s.SetDumpRouteMap(false) + s.Start() + defer s.Shutdown() + + // 等待启动完成 + time.Sleep(time.Second) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) + + gtest.Assert(client.GetContent("/"), "12") + gtest.Assert(client.GetContent("/test/test"), "1test2") + }) +}