gf/net/ghttp/ghttp_server_router_serve.go

207 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}