// 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 import ( "encoding/json" "fmt" "strings" "github.com/gogf/gf/container/glist" "github.com/gogf/gf/text/gregex" ) // 缓存数据项 type handlerCacheItem struct { parsedItems []*handlerParsedItem hasHook bool hasServe bool } // 查询请求处理方法. // 内部带锁机制,可以并发读,但是不能并发写;并且有缓存机制,按照Host、Method、Path进行缓存. func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) { value := s.serveCache.GetOrSetFunc(s.serveHandlerKey(r.Method, r.URL.Path, r.GetHost()), func() interface{} { parsedItems, hasHook, hasServe = s.searchHandlers(r.Method, r.URL.Path, r.GetHost()) if parsedItems != nil { return &handlerCacheItem{parsedItems, hasHook, hasServe} } return nil }, s.config.RouterCacheExpire*1000) if value != nil { item := value.(*handlerCacheItem) return item.parsedItems, item.hasHook, item.hasServe } return } // 路由注册方法检索,返回所有该路由的注册函数,构造成数组返回 func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) { if len(path) == 0 { return nil, false, false } // 遍历检索的域名列表,优先遍历默认域名 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:], "/") } parsedItemList := glist.New() lastMiddlewareItem := (*glist.Element)(nil) for _, domain := range domains { p, ok := s.serveTree[domain] if !ok { continue } //gutil.Dump(p) // 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理 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"].(*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"].(*glist.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"].(*glist.List)) } } } // 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高 for i := len(lists) - 1; i >= 0; i-- { for e := lists[i].Front(); e != nil; e = e.Next() { item := e.Value.(*handlerItem) // 服务路由函数只能添加一次 if hasServe { switch item.itemType { case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: continue } } // 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储 if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, method) { // 注意当不带任何动态路由规则时,len(match) == 1 if match, err := gregex.MatchString(item.router.RegRule, path); err == nil && len(match) > 0 { parsedItem := &handlerParsedItem{item, nil} // 如果需要query匹配,那么需要重新正则解析URL if len(item.router.RegNames) > 0 { if len(match) > len(item.router.RegNames) { parsedItem.values = make(map[string]string) // 如果存在存在同名路由参数名称,那么执行覆盖 for i, name := range item.router.RegNames { parsedItem.values[name] = match[i+1] } } } switch item.itemType { // 服务路由函数只能添加一次 case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: hasServe = true parsedItemList.PushBack(parsedItem) // 中间件需要排序 case gHANDLER_TYPE_MIDDLEWARE: if lastMiddlewareItem == nil { lastMiddlewareItem = parsedItemList.PushFront(parsedItem) } else { lastMiddlewareItem = parsedItemList.InsertAfter(parsedItem, lastMiddlewareItem) } // 钩子函数存在性判断 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 } // MarshalJSON implements the interface MarshalJSON for json.Marshal. func (item *handlerItem) MarshalJSON() ([]byte, error) { switch item.itemType { case gHANDLER_TYPE_HOOK: return json.Marshal( fmt.Sprintf( `%s %s:%s (%s)`, item.router.Uri, item.router.Domain, item.router.Method, item.hookName, ), ) case gHANDLER_TYPE_MIDDLEWARE: return json.Marshal( fmt.Sprintf( `%s %s:%s (MIDDLEWARE)`, item.router.Uri, item.router.Domain, item.router.Method, ), ) default: return json.Marshal( fmt.Sprintf( `%s %s:%s`, item.router.Uri, item.router.Domain, item.router.Method, ), ) } } // MarshalJSON implements the interface MarshalJSON for json.Marshal. func (item *handlerParsedItem) MarshalJSON() ([]byte, error) { return json.Marshal(item.handler) } // 生成回调方法查询的Key func (s *Server) serveHandlerKey(method, path, domain string) string { if method == "" { return path + "@" + strings.ToLower(domain) } return strings.ToUpper(method) + ":" + path + "@" + strings.ToLower(domain) }