add middleware feature for ghttp.Server

This commit is contained in:
John
2019-08-03 18:08:10 +08:00
parent 878305fee3
commit f4d01f56c6
5 changed files with 135 additions and 47 deletions

View File

@ -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
}

View File

@ -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批量绑定回调函数

View File

@ -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,
})
}
}

View File

@ -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
}

View File

@ -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")
})
}