diff --git a/g/net/ghttp/http_server.go b/g/net/ghttp/http_server.go index 28b594192..32807d262 100644 --- a/g/net/ghttp/http_server.go +++ b/g/net/ghttp/http_server.go @@ -19,6 +19,7 @@ import ( "gitee.com/johng/gf/g/container/glist" "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/os/gcache" + "gitee.com/johng/gf/g/util/gregx" ) const ( @@ -150,32 +151,18 @@ func (s *Server) setHandler(domain, method, pattern string, item HandlerItem) { } -// 查询请求处理方法 -func (s *Server) getHandler(domain, method, pattern string) *HandlerItem { - s.hmu.RLock() - defer s.hmu.RUnlock() - key := s.handlerKey(domain, method, pattern) - if f, ok := s.handlerMap[key]; ok { - return &f - } - return nil -} - // 解析pattern -func (s *Server)parsePattern(pattern string) (domain, method, uri string, err error) { - uri = "" +func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri string, err error) { + uri = pattern domain = gDEFAULT_DOMAIN method = "all" - result := strings.Split(pattern, "@") - if len(result) > 1 { - domain = result[1] + if array, err := gregx.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil { + method = array[1] + pattern = array[2] } - result = strings.Split(result[0], ":") - if len(result) > 1 { - method = result[0] - uri = result[1] - } else { - uri = result[0] + if array, err := gregx.MatchString(`(.+)@([\w\.\-]+)`, pattern); len(array) > 1 && err == nil { + uri = array[1] + domain = array[2] } if uri == "" { err = errors.New("invalid pattern") @@ -190,7 +177,7 @@ func (s *Server)bindHandlerItem(pattern string, item HandlerItem) error { if s.status == 1 { return errors.New("server handlers cannot be changed while running") } - domain, method, uri, err := s.parsePattern(pattern) + domain, method, uri, err := s.parsePatternForBindHandler(pattern) if err != nil { return errors.New("invalid pattern") } @@ -346,7 +333,7 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin // 绑定指定的hook回调函数, hook参数的值由ghttp server设定,参数不区分大小写 // 目前hook支持:Init/Shut func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error { - domain, method, uri, err := s.parsePattern(pattern) + domain, method, uri, err := s.parsePatternForBindHandler(pattern) if err != nil { return errors.New("invalid pattern") } diff --git a/g/net/ghttp/http_server_handler.go b/g/net/ghttp/http_server_handler.go index b710e6ba4..e19318773 100644 --- a/g/net/ghttp/http_server_handler.go +++ b/g/net/ghttp/http_server_handler.go @@ -19,6 +19,8 @@ import ( "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/encoding/ghtml" "gitee.com/johng/gf/g/container/gtype" + "gitee.com/johng/gf/g/util/gregx" + "strconv" ) // 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行 @@ -31,7 +33,7 @@ func (s *Server)defaultHttpHandle(w http.ResponseWriter, r *http.Request) { // 其次,如果没有对应的自定义处理接口配置,那么走默认的域名处理接口配置; // 最后,如果以上都没有找到处理接口,那么进行文件处理; func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { - // 路由解析 + // 全局路由解析 uri := r.URL.String() result, err := s.Router.Dispatch(uri) if err == nil && strings.Compare(uri, result) != 0 { @@ -47,15 +49,78 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { ResponseWriter : w, }, } - if h := s.getHandler(gDEFAULT_DOMAIN, r.Method, r.URL.Path); h != nil { + if h := s.getHandler(request); h != nil { s.callHandler(h, request) } else { - if h := s.getHandler(strings.Split(r.Host, ":")[0], r.Method, r.URL.Path); h != nil { - s.callHandler(h, request) - } else { - s.serveFile(w, r) + s.serveFile(w, r) + } +} + +// 查询请求处理方法 +func (s *Server) getHandler(r *Request) *HandlerItem { + s.hmu.RLock() + defer s.hmu.RUnlock() + domains := []string{gDEFAULT_DOMAIN, strings.Split(r.Host, ":")[0]} + // 首先进行静态匹配 + for _, domain := range domains { + if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok { + return &f } } + // 其次进行正则匹配(会比较耗效率) + for k, v := range s.handlerMap { + if array, err := gregx.MatchString(`([a-zA-Z]+):(.+)@([\w\.\-]+)`, k); len(array) > 2 && err == nil { + // method匹配 + if strings.EqualFold(r.Method, array[1]) { + for _, domain := range domains { + // domain匹配 + if !strings.EqualFold(domain, array[3]) { + continue + } + // method & domain匹配时,那么执行pattern的正则匹配 + regrule, querystr := s.patternToRegRule(array[2]) + if gregx.IsMatchString(regrule, r.URL.Path) { + // 如果需要query匹配,那么需要重新解析URL + if len(querystr) > 0 { + if result, err := gregx.ReplaceString(regrule, querystr, r.URL.Path); err == nil { + r.URL, _ = r.URL.Parse(r.URL.Path + "?" + r.URL.RawQuery + "&" + result) + } + } + return &v + } + } + } + } + } + return nil +} + +// 将pattern(不带method和domain)解析成正则表达式匹配以及对应的query字符串 +func (s *Server) patternToRegRule(rule string) (regrule string, querystr string) { + regrule = "/" + array := strings.Split(rule[1:], "/") + index := 1 + for _, v := range array { + switch v[0] { + case ':': + regrule += `/([\w\.\-]+)` + if len(querystr) > 0 { + querystr += "&" + } + querystr += v[1:] + "=$" + strconv.Itoa(index) + index++ + case '*': + regrule += `/(.*)` + if len(querystr) > 0 { + querystr += "&" + } + querystr += v[1:] + "=$" + strconv.Itoa(index) + return + default: + regrule += v + } + } + return } // 初始化控制器 diff --git a/g/net/grouter/grouter.go b/g/net/grouter/grouter.go index aae76d273..e4b0f7684 100644 --- a/g/net/grouter/grouter.go +++ b/g/net/grouter/grouter.go @@ -25,8 +25,8 @@ type Router struct { pmu sync.RWMutex // 打包规则互斥锁 dkeys []string // 解析规则排序键名 pkeys []string // 打包规则排序键名 - drules *gmap.StringStringMap // 解析规则 - prules *gmap.StringStringMap // 打包规则 + drules *gmap.StringStringMap // dispatch解析规则 + prules *gmap.StringStringMap // patch打包规则 } func New() *Router { diff --git a/g/util/gregx/regx.go b/g/util/gregx/regx.go index 42ad695f7..1a11f4023 100644 --- a/g/util/gregx/regx.go +++ b/g/util/gregx/regx.go @@ -11,6 +11,12 @@ import ( "regexp" ) +// 校验所给定的正则表达式是否符合规范 +func Validate(pattern string) error { + _, err := regexp.Compile(pattern) + return err +} + // 正则表达式是否匹配 func IsMatch(pattern string, src []byte) bool { match, err := regexp.Match(pattern, src) diff --git a/geg/net/ghttp/router.go b/geg/net/ghttp/router.go new file mode 100644 index 000000000..49309bec8 --- /dev/null +++ b/geg/net/ghttp/router.go @@ -0,0 +1,11 @@ +package main + +import "gitee.com/johng/gf/g/net/ghttp" + +func main () { + ghttp.GetServer().BindHandler("/router/:name", func(r *ghttp.Request) { + r.Response.WriteString(r.GetQueryString("name")) + }) + ghttp.GetServer().SetPort(10000) + ghttp.GetServer().Run() +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 52baaa236..c003da880 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,10 +1,42 @@ package main import ( - "gitee.com/johng/gf/g/os/glog" + "strings" + "fmt" + "gitee.com/johng/gf/g/util/gregx" + "strconv" ) -func main() { - glog.Println(1) +func ruleToRegx(rule string) (regrule string, querystr string) { + regrule = "/" + array := strings.Split(rule[1:], "/") + index := 1 + for _, v := range array { + switch v[0] { + case ':': + regrule += `/([\w\.\-]+)` + if len(querystr) > 0 { + querystr += "&" + } + querystr += v[1:] + "=$" + strconv.Itoa(index) + index++ + case '*': + regrule += `/(.*)` + if len(querystr) > 0 { + querystr += "&" + } + querystr += v[1:] + "=$" + strconv.Itoa(index) + return + default: + regrule += v + } + } + return +} +func main() { + fmt.Println(strings.Compare("A", "A")) + fmt.Println(gregx.MatchString(`(\w+):(.+)@([\w\.\-]+)`, "get/users/name/*action@www.johng-cn.com")) + fmt.Println(ruleToRegx("/users/:name/*action")) + fmt.Println(gregx.IsMatch(`/users/([\w\.\-]+)/(.*)`, []byte("/users/john/aa/aa/aa"))) } \ No newline at end of file