github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server_router_hook.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  // 事件回调(中间件)路由控制.
     7  
     8  package ghttp
     9  
    10  import (
    11  	"container/list"
    12  	"fmt"
    13  	"github.com/zhongdalu/gf/g/container/gset"
    14  	"github.com/zhongdalu/gf/g/text/gregex"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  )
    19  
    20  // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写
    21  func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) {
    22  	s.setHandler(pattern, &handlerItem{
    23  		name:  runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
    24  		ctype: nil,
    25  		fname: "",
    26  		faddr: handler,
    27  	}, hook)
    28  }
    29  
    30  // 通过map批量绑定回调函数
    31  func (s *Server) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) {
    32  	for k, v := range hookmap {
    33  		s.BindHookHandler(pattern, k, v)
    34  	}
    35  }
    36  
    37  // 事件回调处理,内部使用了缓存处理.
    38  // 并按照指定hook回调函数的优先级及注册顺序进行调用
    39  func (s *Server) callHookHandler(hook string, r *Request) {
    40  	// 如果没有hook注册,那么不用执行后续逻辑
    41  	if len(s.hooksTree) == 0 {
    42  		return
    43  	}
    44  	hookItems := s.getHookHandlerWithCache(hook, r)
    45  	if len(hookItems) > 0 {
    46  		// 备份原有的router变量
    47  		oldRouterVars := r.routerVars
    48  		for _, item := range hookItems {
    49  			// hook方法不能更改serve方法的路由参数,其匹配的路由参数只能自己使用,
    50  			// 且在多个hook方法之间不能共享路由参数,单可以使用匹配的serve方法路由参数。
    51  			// 当前回调函数的路由参数只在当前回调函数下有效。
    52  			r.routerVars = make(map[string][]string)
    53  			if len(oldRouterVars) > 0 {
    54  				for k, v := range oldRouterVars {
    55  					r.routerVars[k] = v
    56  				}
    57  			}
    58  			if len(item.values) > 0 {
    59  				for k, v := range item.values {
    60  					r.routerVars[k] = v
    61  				}
    62  			}
    63  			// 不使用hook的router对象,保留路由注册服务的router对象,不能覆盖
    64  			// r.Router = item.handler.router
    65  			if err := s.niceCallHookHandler(item.handler.faddr, r); err != nil {
    66  				switch err {
    67  				case gEXCEPTION_EXIT:
    68  					break
    69  				case gEXCEPTION_EXIT_ALL:
    70  					fallthrough
    71  				case gEXCEPTION_EXIT_HOOK:
    72  					return
    73  				default:
    74  					panic(err)
    75  				}
    76  			}
    77  		}
    78  		// 恢复原有的router变量
    79  		r.routerVars = oldRouterVars
    80  	}
    81  }
    82  
    83  // 友好地调用方法
    84  func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{}) {
    85  	defer func() {
    86  		err = recover()
    87  	}()
    88  	f(r)
    89  	return
    90  }
    91  
    92  // 查询请求处理方法, 带缓存机制,按照Host、Method、Path进行缓存.
    93  func (s *Server) getHookHandlerWithCache(hook string, r *Request) []*handlerParsedItem {
    94  	cacheItems := ([]*handlerParsedItem)(nil)
    95  	cacheKey := s.handlerKey(hook, r.Method, r.URL.Path, r.GetHost())
    96  	if v := s.hooksCache.Get(cacheKey); v == nil {
    97  		cacheItems = s.searchHookHandler(r.Method, r.URL.Path, r.GetHost(), hook)
    98  		if cacheItems != nil {
    99  			s.hooksCache.Set(cacheKey, cacheItems, s.config.RouterCacheExpire*1000)
   100  		}
   101  	} else {
   102  		cacheItems = v.([]*handlerParsedItem)
   103  	}
   104  	return cacheItems
   105  }
   106  
   107  // 事件方法检索
   108  func (s *Server) searchHookHandler(method, path, domain, hook string) []*handlerParsedItem {
   109  	if len(path) == 0 {
   110  		return nil
   111  	}
   112  	// 遍历检索的域名列表
   113  	domains := []string{gDEFAULT_DOMAIN}
   114  	if !strings.EqualFold(gDEFAULT_DOMAIN, domain) {
   115  		domains = append(domains, domain)
   116  	}
   117  	// URL.Path层级拆分
   118  	array := ([]string)(nil)
   119  	if strings.EqualFold("/", path) {
   120  		array = []string{"/"}
   121  	} else {
   122  		array = strings.Split(path[1:], "/")
   123  	}
   124  	parsedItems := make([]*handlerParsedItem, 0)
   125  	for _, domain := range domains {
   126  		p, ok := s.hooksTree[domain]
   127  		if !ok {
   128  			continue
   129  		}
   130  		p, ok = p.(map[string]interface{})[hook]
   131  		if !ok {
   132  			continue
   133  		}
   134  		// 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
   135  		lists := make([]*list.List, 0)
   136  		for k, v := range array {
   137  			if _, ok := p.(map[string]interface{})["*list"]; ok {
   138  				lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
   139  			}
   140  			if _, ok := p.(map[string]interface{})[v]; ok {
   141  				p = p.(map[string]interface{})[v]
   142  				if k == len(array)-1 {
   143  					if _, ok := p.(map[string]interface{})["*list"]; ok {
   144  						lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
   145  						break
   146  					}
   147  				}
   148  			} else {
   149  				if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
   150  					p = p.(map[string]interface{})["*fuzz"]
   151  				}
   152  			}
   153  			// 如果是叶子节点,同时判断当前层级的"*fuzz"键名,解决例如:/user/*action 匹配 /user 的规则
   154  			if k == len(array)-1 {
   155  				if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
   156  					p = p.(map[string]interface{})["*fuzz"]
   157  				}
   158  				if _, ok := p.(map[string]interface{})["*list"]; ok {
   159  					lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
   160  				}
   161  			}
   162  		}
   163  
   164  		// 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高
   165  		pushedSet := gset.NewStringSet(true)
   166  		for i := len(lists) - 1; i >= 0; i-- {
   167  			for e := lists[i].Front(); e != nil; e = e.Next() {
   168  				handler := e.Value.(*handlerItem)
   169  				// 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储
   170  				if strings.EqualFold(handler.router.Method, gDEFAULT_METHOD) || strings.EqualFold(handler.router.Method, method) {
   171  					// 注意当不带任何动态路由规则时,len(match) == 1
   172  					if match, err := gregex.MatchString(handler.router.RegRule, path); err == nil && len(match) > 0 {
   173  						parsedItem := &handlerParsedItem{handler, nil}
   174  						// 如果需要query匹配,那么需要重新正则解析URL
   175  						if len(handler.router.RegNames) > 0 {
   176  							if len(match) > len(handler.router.RegNames) {
   177  								parsedItem.values = make(map[string][]string)
   178  								// 如果存在存在同名路由参数名称,那么执行数组追加
   179  								for i, name := range handler.router.RegNames {
   180  									if _, ok := parsedItem.values[name]; ok {
   181  										parsedItem.values[name] = append(parsedItem.values[name], match[i+1])
   182  									} else {
   183  										parsedItem.values[name] = []string{match[i+1]}
   184  									}
   185  								}
   186  							}
   187  						}
   188  						address := fmt.Sprintf("%p", handler)
   189  						if !pushedSet.Contains(address) {
   190  							parsedItems = append(parsedItems, parsedItem)
   191  							pushedSet.Add(address)
   192  						}
   193  					}
   194  				}
   195  			}
   196  		}
   197  		return parsedItems
   198  	}
   199  	return nil
   200  }
   201  
   202  // 生成hook key,如果是hook key,那么使用'%'符号分隔
   203  func (s *Server) handlerKey(hook, method, path, domain string) string {
   204  	return hook + "%" + s.serveHandlerKey(method, path, domain)
   205  }