diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index d5cf37a48..ac1098a50 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -9,7 +9,6 @@ package ghttp import ( "io/ioutil" "net/http" - "gitee.com/johng/gf/g/util/gconv" "gitee.com/johng/gf/g/encoding/gjson" "gitee.com/johng/gf/g/container/gtype" "gitee.com/johng/gf/g/util/gregex" @@ -21,7 +20,8 @@ type Request struct { http.Request parsedGet *gtype.Bool // GET参数是否已经解析 parsedPost *gtype.Bool // POST参数是否已经解析 - queries map[string][]string // GET参数 + queryVars map[string][]string // GET参数 + routerVars map[string][]string // 路由解析参数 exit *gtype.Bool // 是否退出当前请求流程执行 Id int // 请求id(唯一) Server *Server // 请求关联的服务器对象 @@ -41,7 +41,8 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { request := &Request{ parsedGet : gtype.NewBool(), parsedPost : gtype.NewBool(), - queries : make(map[string][]string), + queryVars : make(map[string][]string), + routerVars : make(map[string][]string), exit : gtype.NewBool(), Id : s.servedCount.Add(1), Server : s, @@ -58,29 +59,6 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { return request } -// 初始化GET请求参数 -func (r *Request) initGet() { - if !r.parsedGet.Val() { - if len(r.queries) == 0 { - r.queries = r.URL.Query() - } else { - for k, v := range r.URL.Query() { - r.queries[k] = v - } - } - } -} - -// 初始化POST请求参数 -func (r *Request) initPost() { - if !r.parsedPost.Val() { - // 快速保存,尽量避免并发问题 - r.parsedPost.Set(true) - // MultiMedia表单请求解析允许最大使用内存:1GB - r.ParseMultipartForm(1024*1024*1024) - } -} - // 获取Web Socket连接对象 func (r *Request) WebSocket() (*WebSocket, error) { if conn, err := wsUpgrader.Upgrade(r.Response.ResponseWriter.ResponseWriter, &r.Request, nil); err == nil { @@ -92,203 +70,12 @@ func (r *Request) WebSocket() (*WebSocket, error) { } } -// 获得指定名称的参数字符串(GET/POST),同 GetRequestString +// 获得指定名称的参数字符串(Router/GET/POST),同 GetRequestString // 这是常用方法的简化别名 func (r *Request) Get(k string) string { return r.GetRequestString(k) } -// 获得指定名称的get参数列表 -func (r *Request) GetQuery(k string) []string { - r.initGet() - if v, ok := r.queries[k]; ok { - return v - } - return nil -} - -func (r *Request) GetQueryBool(k string) bool { - return gconv.Bool(r.Get(k)) -} - -func (r *Request) GetQueryInt(k string) int { - return gconv.Int(r.Get(k)) -} - -func (r *Request) GetQueryUint(k string) uint { - return gconv.Uint(r.Get(k)) -} - -func (r *Request) GetQueryFloat32(k string) float32 { - return gconv.Float32(r.Get(k)) -} - -func (r *Request) GetQueryFloat64(k string) float64 { - return gconv.Float64(r.Get(k)) -} - -func (r *Request) GetQueryString(k string) string { - v := r.GetQuery(k) - if v == nil { - return "" - } else { - return v[0] - } -} - -func (r *Request) GetQueryArray(k string) []string { - return r.GetQuery(k) -} - -// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -func (r *Request) GetQueryMap(defaultMap...map[string]string) map[string]string { - r.initGet() - m := make(map[string]string) - if len(defaultMap) == 0 { - for k, v := range r.queries { - m[k] = v[0] - } - } else { - for k, v := range defaultMap[0] { - v2 := r.GetQueryArray(k) - if v2 == nil { - m[k] = v - } else { - m[k] = v2[0] - } - } - } - return m -} - -// 获得post参数 -func (r *Request) GetPost(k string) []string { - r.initPost() - if v, ok := r.PostForm[k]; ok { - return v - } - return nil -} - -func (r *Request) GetPostBool(k string) bool { - return gconv.Bool(r.GetPostString(k)) -} - -func (r *Request) GetPostInt(k string) int { - return gconv.Int(r.GetPostString(k)) -} - -func (r *Request) GetPostUint(k string) uint { - return gconv.Uint(r.GetPostString(k)) -} - -func (r *Request) GetPostFloat32(k string) float32 { - return gconv.Float32(r.GetPostString(k)) -} - -func (r *Request) GetPostFloat64(k string) float64 { - return gconv.Float64(r.GetPostString(k)) -} - -func (r *Request) GetPostString(k string) string { - v := r.GetPost(k) - if v == nil { - return "" - } else { - return v[0] - } -} - -func (r *Request) GetPostArray(k string) []string { - return r.GetPost(k) -} - -// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetPostArray获取特定字段内容 -func (r *Request) GetPostMap(defaultMap...map[string]string) map[string]string { - r.initPost() - m := make(map[string]string) - if len(defaultMap) == 0 { - for k, v := range r.PostForm { - m[k] = v[0] - } - } else { - for k, v := range defaultMap[0] { - if v2, ok := r.PostForm[k]; ok { - m[k] = v2[0] - } else { - m[k] = v - } - } - } - return m -} - -// 获得post或者get提交的参数,如果有同名参数,那么按照get->post优先级进行覆盖 -func (r *Request) GetRequest(k string) []string { - v := r.GetQuery(k) - if v == nil { - return r.GetPost(k) - } - return v -} - -func (r *Request) GetRequestString(k string) string { - v := r.GetRequest(k) - if v == nil { - return "" - } else { - return v[0] - } -} - -func (r *Request) GetRequestBool(k string) bool { - return gconv.Bool(r.GetRequestString(k)) -} - -func (r *Request) GetRequestInt(k string) int { - return gconv.Int(r.GetRequestString(k)) -} - -func (r *Request) GetRequestUint(k string) uint { - return gconv.Uint(r.GetRequestString(k)) -} - -func (r *Request) GetRequestFloat32(k string) float32 { - return gconv.Float32(r.GetRequestString(k)) -} - -func (r *Request) GetRequestFloat64(k string) float64 { - return gconv.Float64(r.GetRequestString(k)) -} - -func (r *Request) GetRequestArray(k string) []string { - return r.GetRequest(k) -} - -// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetRequestArray获取特定字段内容 -func (r *Request) GetRequestMap(defaultMap...map[string]string) map[string]string { - m := r.GetQueryMap() - if len(defaultMap) == 0 { - for k, v := range r.GetPostMap() { - if _, ok := m[k]; !ok { - m[k] = v - } - } - } else { - for k, v := range defaultMap[0] { - v2 := r.GetRequest(k) - if v2 != nil { - m[k] = v2[0] - } else { - m[k] = v - } - } - } - return m -} - // 获取原始请求输入字符串,注意:只能获取一次,读完就没了 func (r *Request) GetRaw() []byte { result, _ := ioutil.ReadAll(r.Body) diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go new file mode 100644 index 000000000..787f1397b --- /dev/null +++ b/g/net/ghttp/ghttp_request_post.go @@ -0,0 +1,84 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +package ghttp + +import ( + "gitee.com/johng/gf/g/util/gconv" +) + +// 初始化POST请求参数 +func (r *Request) initPost() { + if !r.parsedPost.Val() { + // 快速保存,尽量避免并发问题 + r.parsedPost.Set(true) + // MultiMedia表单请求解析允许最大使用内存:1GB + r.ParseMultipartForm(1024*1024*1024) + } +} + +// 获得post参数 +func (r *Request) GetPost(k string) []string { + r.initPost() + if v, ok := r.PostForm[k]; ok { + return v + } + return nil +} + +func (r *Request) GetPostBool(k string) bool { + return gconv.Bool(r.GetPostString(k)) +} + +func (r *Request) GetPostInt(k string) int { + return gconv.Int(r.GetPostString(k)) +} + +func (r *Request) GetPostUint(k string) uint { + return gconv.Uint(r.GetPostString(k)) +} + +func (r *Request) GetPostFloat32(k string) float32 { + return gconv.Float32(r.GetPostString(k)) +} + +func (r *Request) GetPostFloat64(k string) float64 { + return gconv.Float64(r.GetPostString(k)) +} + +func (r *Request) GetPostString(k string) string { + v := r.GetPost(k) + if v == nil { + return "" + } else { + return v[0] + } +} + +func (r *Request) GetPostArray(k string) []string { + return r.GetPost(k) +} + +// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 +// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetPostArray获取特定字段内容 +func (r *Request) GetPostMap(defaultMap...map[string]string) map[string]string { + r.initPost() + m := make(map[string]string) + if len(defaultMap) == 0 { + for k, v := range r.PostForm { + m[k] = v[0] + } + } else { + for k, v := range defaultMap[0] { + if v2, ok := r.PostForm[k]; ok { + m[k] = v2[0] + } else { + m[k] = v + } + } + } + return m +} diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go new file mode 100644 index 000000000..97302258a --- /dev/null +++ b/g/net/ghttp/ghttp_request_query.go @@ -0,0 +1,88 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +package ghttp + +import ( + "gitee.com/johng/gf/g/util/gconv" +) + +// 初始化GET请求参数 +func (r *Request) initGet() { + if !r.parsedGet.Val() { + if len(r.queryVars) == 0 { + r.queryVars = r.URL.Query() + } else { + for k, v := range r.URL.Query() { + r.queryVars[k] = v + } + } + } +} + +// 获得指定名称的get参数列表 +func (r *Request) GetQuery(k string) []string { + r.initGet() + if v, ok := r.queryVars[k]; ok { + return v + } + return nil +} + +func (r *Request) GetQueryBool(k string) bool { + return gconv.Bool(r.Get(k)) +} + +func (r *Request) GetQueryInt(k string) int { + return gconv.Int(r.Get(k)) +} + +func (r *Request) GetQueryUint(k string) uint { + return gconv.Uint(r.Get(k)) +} + +func (r *Request) GetQueryFloat32(k string) float32 { + return gconv.Float32(r.Get(k)) +} + +func (r *Request) GetQueryFloat64(k string) float64 { + return gconv.Float64(r.Get(k)) +} + +func (r *Request) GetQueryString(k string) string { + v := r.GetQuery(k) + if v == nil { + return "" + } else { + return v[0] + } +} + +func (r *Request) GetQueryArray(k string) []string { + return r.GetQuery(k) +} + +// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 +func (r *Request) GetQueryMap(defaultMap...map[string]string) map[string]string { + r.initGet() + m := make(map[string]string) + if len(defaultMap) == 0 { + for k, v := range r.queryVars { + m[k] = v[0] + } + } else { + for k, v := range defaultMap[0] { + v2 := r.GetQueryArray(k) + if v2 == nil { + m[k] = v + } else { + m[k] = v2[0] + } + } + } + return m +} + diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go new file mode 100644 index 000000000..281cffde1 --- /dev/null +++ b/g/net/ghttp/ghttp_request_request.go @@ -0,0 +1,79 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +package ghttp + +import ( + "gitee.com/johng/gf/g/util/gconv" +) + +// 获得router、post或者get提交的参数,如果有同名参数,那么按照router->get->post优先级进行覆盖 +func (r *Request) GetRequest(k string) []string { + v := r.GetRouterArray(k) + if v == nil { + v = r.GetQuery(k) + } + if v == nil { + v = r.GetPost(k) + } + return v +} + +func (r *Request) GetRequestString(k string) string { + v := r.GetRequest(k) + if v == nil { + return "" + } else { + return v[0] + } +} + +func (r *Request) GetRequestBool(k string) bool { + return gconv.Bool(r.GetRequestString(k)) +} + +func (r *Request) GetRequestInt(k string) int { + return gconv.Int(r.GetRequestString(k)) +} + +func (r *Request) GetRequestUint(k string) uint { + return gconv.Uint(r.GetRequestString(k)) +} + +func (r *Request) GetRequestFloat32(k string) float32 { + return gconv.Float32(r.GetRequestString(k)) +} + +func (r *Request) GetRequestFloat64(k string) float64 { + return gconv.Float64(r.GetRequestString(k)) +} + +func (r *Request) GetRequestArray(k string) []string { + return r.GetRequest(k) +} + +// 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 +// 需要注意的是,如果其中一个字段为数组形式,那么只会返回第一个元素,如果需要获取全部的元素,请使用GetRequestArray获取特定字段内容 +func (r *Request) GetRequestMap(defaultMap...map[string]string) map[string]string { + m := r.GetQueryMap() + if len(defaultMap) == 0 { + for k, v := range r.GetPostMap() { + if _, ok := m[k]; !ok { + m[k] = v + } + } + } else { + for k, v := range defaultMap[0] { + v2 := r.GetRequest(k) + if v2 != nil { + m[k] = v2[0] + } else { + m[k] = v + } + } + } + return m +} diff --git a/g/net/ghttp/ghttp_request_router.go b/g/net/ghttp/ghttp_request_router.go new file mode 100644 index 000000000..c2d0d3ff9 --- /dev/null +++ b/g/net/ghttp/ghttp_request_router.go @@ -0,0 +1,24 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +package ghttp + +// 获得路由解析参数 +func (r *Request) GetRouterString(k string) string { + if v := r.GetRouterArray(k); v != nil { + return v[0] + } + return "" +} + +// 获得路由解析参数 +func (r *Request) GetRouterArray(k string) []string { + if v, ok := r.routerVars[k]; ok { + return v + } + return nil +} + diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 0da6d9291..b59319db2 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -27,6 +27,7 @@ import ( "github.com/gorilla/websocket" "gitee.com/johng/gf/g/os/gtime" "time" + "container/list" ) const ( @@ -38,6 +39,8 @@ const ( gDEFAULT_COOKIE_MAX_AGE = 86400*365 // 默认cookie有效期(一年) gDEFAULT_SESSION_MAX_AGE = 600 // 默认session有效期(600秒) gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称 + gSERVER_STATUS_STOPPED = 0 // Server状态:停止 + gSERVER_STATUS_RUNNING = 1 // Server状态:运行 ) // ghttp.Server结构体 @@ -52,11 +55,9 @@ type Server struct { servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) // 服务注册相关 - hmmu sync.RWMutex // handler互斥锁 - hmcmu sync.RWMutex // handlerCache互斥锁 - handlerMap HandlerMap // 所有注册的回调函数(静态匹配) - handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配) + handlerTree map[string]interface{} // 所有注册的回调函数(路由表,树型结构,哈希表+链表优先级匹配) handlerCache *gcache.Cache // 服务注册路由内存缓存 + hooksCache *gcache.Cache // 事件回调路由内存缓存 // 自定义状态码回调 hsmu sync.RWMutex // status handler互斥锁 statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) @@ -76,9 +77,6 @@ type Server struct { errorLogger *glog.Logger // error log日志对象 } -// 域名、URI与回调函数的绑定记录表 -type HandlerMap map[string]*handlerRegisterItem - // 路由对象 type Router struct { Uri string // 注册时的pattern - uri @@ -89,19 +87,36 @@ type Router struct { Priority int // 优先级,用于链表排序,值越大优先级越高 } +// 域名、URI与回调函数的绑定记录表 +type handlerMap map[string]*handlerItem + // http回调函数注册信息 -type HandlerItem struct { - ctype reflect.Type // 控制器类型 +type handlerItem struct { + ctype reflect.Type // 控制器类型(反射类型) fname string // 回调方法名称 faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一) } +// 路由注册项(这里使用了非并发安全的list.List,因为该对象的使用统一是由htmu互斥锁保证并发安全) +type handlerRegisterItem struct { + handler *handlerItem // 准确的执行方法内存地址 + hooks map[string]*list.List // 当前的事件回调注册,键名为事件名称,键值为事件执行方法链表 + router *Router // 注册时绑定的路由对象 +} + +// 根据特定URL.Path解析后的路由检索结果项 +type handlerParsedItem struct { + item *handlerRegisterItem // 路由注册项 + values map[string][]string // 特定URL.Path的Router解析参数 +} + // HTTP注册函数 type HandlerFunc func(r *Request) // 文件描述符map type listenerFdMap map[string]string + // Server表,用以存储和检索名称与Server对象之间的关联关系 var serverMapping = gmap.NewStringInterfaceMap() @@ -144,10 +159,8 @@ func GetServer(name...interface{}) (*Server) { paths : gspath.New(), servers : make([]*gracefulServer, 0), methodsMap : make(map[string]bool), - handlerMap : make(HandlerMap), statusHandlerMap : make(map[string]HandlerFunc), handlerTree : make(map[string]interface{}), - hooksTree : make(map[string]interface{}), handlerCache : gcache.New(), hooksCache : gcache.New(), cookies : gmap.NewIntInterfaceMap(), @@ -191,7 +204,7 @@ func (s *Server) Start() error { } } - if s.status == 1 { + if s.status == gSERVER_STATUS_RUNNING { return errors.New("server is already running") } // 底层http server配置 @@ -341,7 +354,7 @@ func (s *Server) startServer(fdMap listenerFdMap) { }(v) } - s.status = 1 + s.status = gSERVER_STATUS_RUNNING } // 获取当前监听的文件描述符信息,构造成map返回 @@ -369,19 +382,3 @@ func (s *Server) getListenerFdMap() map[string]string { return m } - -// 清空当前的handlerCache -func (s *Server) clearHandlerCache() { - s.hmcmu.Lock() - defer s.hmcmu.Unlock() - s.handlerCache.Close() - s.handlerCache = gcache.New() -} - -// 清空当前的hooksCache -func (s *Server) clearHooksCache() { - s.hhcmu.Lock() - defer s.hhcmu.Unlock() - s.hooksCache.Close() - s.hooksCache = gcache.New() -} \ No newline at end of file diff --git a/g/net/ghttp/ghttp_server_domain.go b/g/net/ghttp/ghttp_server_domain.go index 57e604e06..b4d8da2b5 100644 --- a/g/net/ghttp/ghttp_server_domain.go +++ b/g/net/ghttp/ghttp_server_domain.go @@ -41,7 +41,7 @@ func (s *Server) Domain(domain string) *Domain { // 注意该方法是直接绑定方法的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑 func (d *Domain) BindHandler(pattern string, handler HandlerFunc) error { for domain, _ := range d.m { - if err := d.s.bindHandlerItem(pattern + "@" + domain, &HandlerItem{ + if err := d.s.bindHandlerItem(pattern + "@" + domain, &handlerItem{ ctype : nil, fname : "", faddr : handler, diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index 4e5b86bef..96862a334 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -53,7 +53,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { }() // 路由注册检索 - handler := s.getHandler(request) + handler := s.getHandlerWithCache(request) if handler == nil { // 如果路由不匹配,那么执行静态文件检索 path := s.paths.Search(r.URL.Path) @@ -74,7 +74,9 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { s.callHookHandler(request, "BeforeServe") // 执行回调控制器/执行对象/方法 - s.callHandler(handler, request) + if handler.handler != nil { + s.callHandler(handler.handler, request) + } // 事件 - AfterServe s.callHookHandler(request, "AfterServe") @@ -93,7 +95,7 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { } // 初始化控制器 -func (s *Server)callHandler(h *HandlerItem, r *Request) { +func (s *Server)callHandler(h *handlerItem, r *Request) { if h.faddr == nil { // 新建一个控制器对象处理请求 c := reflect.New(h.ctype) diff --git a/g/net/ghttp/ghttp_server_hooks.go b/g/net/ghttp/ghttp_server_hooks.go index 3f88e6fed..d16a01e93 100644 --- a/g/net/ghttp/ghttp_server_hooks.go +++ b/g/net/ghttp/ghttp_server_hooks.go @@ -7,63 +7,54 @@ package ghttp -import ( - "errors" - "strings" - "container/list" - "gitee.com/johng/gf/g/util/gregex" -) - -// hook缓存项,根据URL.Path进行缓存,因此对象中带有缓存参数 -type hookCacheItem struct { - faddr HandlerFunc // 准确的执行方法内存地址 - values map[string][]string // GET解析参数 -} - // 事件回调注册方法 // 因为有事件回调优先级的关系,叶子节点必须为一个链表,因此这里只有动态注册 -func (s *Server) setHookHandler(pattern string, hook string, handler *HandlerItem) error { +func (s *Server) setHookHandler(pattern string, hook string, handler *handlerItem) error { return s.setHandler(pattern, handler, hook) } -// 事件回调 - 检索动态路由规则 +// 检索事件回调方法 +func (s *Server) searchHookHandler(r *Request, hook string) []*handlerItem { + if item := s.getHandlerWithCache(r); item != nil { + if l, ok := item.hooks[hook]; ok { + items := make([]*handlerItem, 0) + for e := l.Front(); e != nil; e = e.Next() { + items = append(items, e.Value.(*handlerItem)) + } + return items + } + } + return nil +} + +// 事件回调处理,内部使用了缓存处理. // 并按照指定hook回调函数的优先级及注册顺序进行调用 func (s *Server) callHookHandler(r *Request, hook string) { - // 如果没有注册事件回调,那么不做后续处理 - if len(s.hooksTree) == 0 { - return - } - - s.hhcmu.RLock() - defer s.hhcmu.RUnlock() - - var hookItems []*hookCacheItem - cacheKey := s.handlerHookKey(r.GetHost(), r.Method, r.URL.Path, hook) + var hookItems []*handlerItem + cacheKey := s.hookHandlerKey(hook, r.Method, r.URL.Path, r.GetHost()) if v := s.hooksCache.Get(cacheKey); v == nil { hookItems = s.searchHookHandler(r, hook) if hookItems != nil { s.hooksCache.Set(cacheKey, hookItems, 0) } } else { - hookItems = v.([]*hookCacheItem) + hookItems = v.([]*handlerItem) } if hookItems != nil { for _, item := range hookItems { - for k, v := range item.values { - r.queries[k] = v - } item.faddr(r) } } } -func (s *Server) searchHookHandler(r *Request, hook string) []*hookCacheItem { - +// 生成hook key +func (s *Server) hookHandlerKey(hook, method, path, domain string) string { + return hook + "@" + s.handlerKey(method, path, domain) } // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error { - return s.setHookHandler(pattern, hook, &HandlerItem{ + return s.setHookHandler(pattern, hook, &handlerItem{ ctype : nil, fname : "", faddr : handler, @@ -80,8 +71,3 @@ func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerF } return nil } - -// 构造用于hooksMap检索的键名 -func (s *Server)handlerHookKey(domain, method, uri, hook string) string { - return strings.ToUpper(hook) + "^" + s.handlerKey(domain, method, uri) -} diff --git a/g/net/ghttp/ghttp_server_router.go b/g/net/ghttp/ghttp_server_router.go index d896a8763..1c1c4486e 100644 --- a/g/net/ghttp/ghttp_server_router.go +++ b/g/net/ghttp/ghttp_server_router.go @@ -12,46 +12,32 @@ import ( "strings" "container/list" "gitee.com/johng/gf/g/util/gregex" - "gitee.com/johng/gf/g/container/glist" + "gitee.com/johng/gf/g/container/gset" + "fmt" ) -// 路由注册项 -type handlerRegisterItem struct { - handler *HandlerItem // 准确的执行方法内存地址 - hooks map[string]*glist.List // 当前的事件回调注册,键名为事件名称,键值为事件执行方法链表 - router *Router // 注册时绑定的路由对象 -} - -// 路由检索缓存项,根据URL.Path进行缓存,因此对象中带有缓存参数 -type handlerCacheItem struct { - item *handlerRegisterItem // 路由注册项 - values map[string][]string // 特定URL.Path的GET解析参数 -} - -// 查询请求处理方法 -// 内部带锁机制,可以并发读,但是不能并发写;并且有缓存机制,按照Host、Method、Path进行缓存 -func (s *Server) getHandler(r *Request) *HandlerItem { - // 缓存清空时是直接修改属性,因此必须使用互斥锁 - s.hmcmu.RLock() - defer s.hmcmu.RUnlock() - - var cacheItem *handlerCacheItem - cacheKey := s.handlerKey(r.GetHost(), r.Method, r.URL.Path) +// 查询请求处理方法. +// 内部带锁机制,可以并发读,但是不能并发写;并且有缓存机制,按照Host、Method、Path进行缓存. +func (s *Server) getHandlerWithCache(r *Request) *handlerRegisterItem { + var cacheItem *handlerParsedItem + cacheKey := s.handlerKey(r.Method, r.URL.Path, r.GetHost()) if v := s.handlerCache.Get(cacheKey); v == nil { - cacheItem = s.searchHandler(r) + cacheItem = s.searchHandler(r.Method, r.URL.Path, r.GetHost()) if cacheItem != nil { s.handlerCache.Set(cacheKey, cacheItem, 0) } } else { - cacheItem = v.(*handlerCacheItem) + cacheItem = v.(*handlerParsedItem) } if cacheItem != nil { - for k, v := range cacheItem.values { - r.queries[k] = v + if r.Router == nil { + for k, v := range cacheItem.values { + r.routerVars[k] = v + } + r.Router = cacheItem.item.router } - r.Router = cacheItem.item.router - return cacheItem.item.handler + return cacheItem.item } return nil } @@ -81,7 +67,11 @@ func (s *Server)parsePattern(pattern string) (domain, method, uri string, err er // 路由注册处理方法。 // 如果带有hook参数,表示是回调注册方法,否则为普通路由执行方法。 -func (s *Server) setHandler(pattern string, handler *HandlerItem, hook ... string) error { +func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... string) error { + // Web Server正字运行时无法动态注册路由方法 + if s.status == gSERVER_STATUS_RUNNING { + return errors.New("cannnot bind handler while server running") + } var hookName string if len(hook) > 0 { hookName = hook[0] @@ -103,166 +93,143 @@ func (s *Server) setHandler(pattern string, handler *HandlerItem, hook ... strin // 注册对象 registerItem := &handlerRegisterItem { handler : handler, - hooks : make(map[string]*glist.List), + hooks : make(map[string]*list.List), router : router, } if len(hookName) > 0 { - registerItem.handler = nil - registerItem.hooks[hookName] = glist.New() - registerItem.hooks[hookName].PushBack(registerItem) + registerItem.handler = nil + registerItem.hooks[hookName] = list.New() + registerItem.hooks[hookName].PushBack(handler) } - s.hmmu.Lock() - defer s.hmmu.Unlock() - defer s.clearHandlerCache() - if s.isPatternUriHasFuzzRule(uri) { - // 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中。 - // 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点; - // 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高; - if _, ok := s.handlerTree[domain]; !ok { - s.handlerTree[domain] = make(map[string]interface{}) + // 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中。 + // 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点; + // 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高; + if _, ok := s.handlerTree[domain]; !ok { + s.handlerTree[domain] = make(map[string]interface{}) + } + // 用于遍历的指针 + p := s.handlerTree[domain] + // 当前节点的规则链表 + lists := make([]*list.List, 0) + array := strings.Split(uri[1:], "/") + // 键名"*fuzz"代表模糊匹配节点,其下会有一个链表; + // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性; + for k, v := range array { + if len(v) == 0 { + continue } - // 用于遍历的指针 - p := s.handlerTree[domain] - // 当前节点的规则链表 - lists := make([]*list.List, 0) - array := strings.Split(uri[1:], "/") - // 键名"*fuzz"代表模糊匹配节点,其下会有一个链表; - // 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性; - for k, v := range array { - if len(v) == 0 { - continue - } - // 判断是否模糊匹配规则 - if gregex.IsMatchString(`^[:\*]|{[\w\.\-]+}`, v) { - v = "*fuzz" - // 由于是模糊规则,因此这里会有一个*list,用以将后续的路由规则加进来, - // 检索会从叶子节点的链表往根节点按照优先级进行检索 - if v, ok := p.(map[string]interface{})["*list"]; !ok { - p.(map[string]interface{})["*list"] = list.New() - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } else { - lists = append(lists, v.(*list.List)) - } - } - // 属性层级数据写入 - if _, ok := p.(map[string]interface{})[v]; !ok { - p.(map[string]interface{})[v] = make(map[string]interface{}) - } - p = p.(map[string]interface{})[v] - // 到达叶子节点,往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表) - if k == len(array) - 1 && v != "*fuzz" { - if v, ok := p.(map[string]interface{})["*list"]; !ok { - p.(map[string]interface{})["*list"] = list.New() - lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) - } else { - lists = append(lists, v.(*list.List)) - } + // 判断是否模糊匹配规则 + if gregex.IsMatchString(`^[:\*]|{[\w\.\-]+}`, v) { + v = "*fuzz" + // 由于是模糊规则,因此这里会有一个*list,用以将后续的路由规则加进来, + // 检索会从叶子节点的链表往根节点按照优先级进行检索 + if v, ok := p.(map[string]interface{})["*list"]; !ok { + p.(map[string]interface{})["*list"] = list.New() + lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + } else { + lists = append(lists, v.(*list.List)) } } - // 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表), - // 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面) - if len(hookName) == 0 { - // 普通方法路由注册 - for _, l := range lists { - pushed := false - for e := l.Front(); e != nil; e = e.Next() { - item := e.Value.(*handlerRegisterItem) - // 判断是否已存在相同的路由注册项 - if strings.EqualFold(router.Domain, item.router.Domain) && - strings.EqualFold(router.Method, item.router.Method) && - strings.EqualFold(router.Uri, item.router.Uri) { - item.handler = handler - pushed = true - break - } - if s.compareRouterPriority(router, item.router) { - l.InsertBefore(registerItem, e) - pushed = true - break - } + // 属性层级数据写入 + if _, ok := p.(map[string]interface{})[v]; !ok { + p.(map[string]interface{})[v] = make(map[string]interface{}) + } + p = p.(map[string]interface{})[v] + // 到达叶子节点,往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表) + if k == len(array) - 1 && v != "*fuzz" { + if v, ok := p.(map[string]interface{})["*list"]; !ok { + p.(map[string]interface{})["*list"] = list.New() + lists = append(lists, p.(map[string]interface{})["*list"].(*list.List)) + } else { + lists = append(lists, v.(*list.List)) + } + } + } + // 得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表), + // 从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在前面) + item := (*handlerRegisterItem)(nil) + // 用以标记 *handlerRegisterItem 指向的对象是否已经处理过,因为多个节点可能会关联同一个该对象 + pushedItemSet := gset.NewStringSet() + if len(hookName) == 0 { + // 普通方法路由注册,追加或者覆盖 + for _, l := range lists { + pushed := false + address := "" + for e := l.Front(); e != nil; e = e.Next() { + item = e.Value.(*handlerRegisterItem) + address = fmt.Sprintf("%p", item) + if pushedItemSet.Contains(address) { + pushed = true + break } - if !pushed { - l.PushBack(registerItem) + // 判断是否已存在相同的路由注册项 + if strings.EqualFold(router.Domain, item.router.Domain) && + strings.EqualFold(router.Method, item.router.Method) && + strings.EqualFold(router.Uri, item.router.Uri) { + item.handler = handler + pushed = true + break + } + if s.compareRouterPriority(router, item.router) { + l.InsertBefore(registerItem, e) + pushed = true + break } } - } else { - // 回调方法路由注册 - for _, l := range lists { - pushed := false - for e := l.Front(); e != nil; e = e.Next() { - item := e.Value.(*handlerRegisterItem) - // 判断是否已存在相同的路由注册项 - if strings.EqualFold(router.Domain, item.router.Domain) && - strings.EqualFold(router.Method, item.router.Method) && - strings.EqualFold(router.Uri, item.router.Uri) { - if _, ok := item.hooks[hookName]; !ok { - item.hooks[hookName] = glist.New() - } - item.hooks[hookName].PushBack(handler) - pushed = true - break - } - if s.compareRouterPriority(router, item.router) { - l.InsertBefore(registerItem, e) - pushed = true - break - } - } - if !pushed { - l.PushBack(registerItem) + if pushed { + if len(address) > 0 { + pushedItemSet.Add(address) } + } else { + l.PushBack(registerItem) } } } else { - // 静态注册 - if len(hookName) == 0 { - // 普通方法注册 - if method == gDEFAULT_METHOD { - for v, _ := range s.methodsMap { - key := s.handlerKey(domain, v, uri) - if v, ok := s.handlerMap[key]; ok { - v.handler = handler - } else { - s.handlerMap[key] = registerItem - } + // 回调方法路由注册,将方法追加到链表末尾 + for _, l := range lists { + pushed := false + address := "" + for e := l.Front(); e != nil; e = e.Next() { + item = e.Value.(*handlerRegisterItem) + address = fmt.Sprintf("%p", item) + if pushedItemSet.Contains(address) { + pushed = true + break } - } else { - key := s.handlerKey(domain, method, uri) - if v, ok := s.handlerMap[key]; ok { - v.handler = handler - } else { - s.handlerMap[key] = registerItem + // 判断是否已存在相同的路由注册项 + if strings.EqualFold(router.Domain, item.router.Domain) && + strings.EqualFold(router.Method, item.router.Method) && + strings.EqualFold(router.Uri, item.router.Uri) { + if _, ok := item.hooks[hookName]; !ok { + item.hooks[hookName] = list.New() + } + item.hooks[hookName].PushBack(handler) + pushed = true + break + } + if s.compareRouterPriority(router, item.router) { + l.InsertBefore(registerItem, e) + pushed = true + break } } - } else { - // 回调方法注册 - if method == gDEFAULT_METHOD { - for v, _ := range s.methodsMap { - key := s.handlerKey(domain, v, uri) - if v, ok := s.handlerMap[key]; ok { - - } else { - s.handlerMap[key] = registerItem - } + if pushed { + if len(address) > 0 { + pushedItemSet.Add(address) } } else { - key := s.handlerKey(domain, method, uri) - if v, ok := s.handlerMap[key]; ok { - v.handler = handler - } else { - s.handlerMap[key] = registerItem - } + l.PushBack(registerItem) } } - } + //b, _ := gparser.VarToJsonIndent(s.handlerTree) //fmt.Println(string(b)) return nil } -// 对比两个HandlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序 +// 对比两个handlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序 // 优先级比较规则: // 1、层级越深优先级越高(对比/数量); // 2、模糊规则优先级:{xxx} > :xxx > *xxx; @@ -289,34 +256,12 @@ func (s *Server) compareRouterPriority(newRouter, oldRouter *Router) bool { } // 服务方法检索 -func (s *Server) searchHandler(r *Request) *handlerCacheItem { - item := s.searchHandlerStatic(r) - if item == nil { - item = s.searchHandlerDynamic(r) +func (s *Server) searchHandler(method, path, domain string) *handlerParsedItem { + domains := []string{ gDEFAULT_DOMAIN } + if !strings.EqualFold(gDEFAULT_DOMAIN, domain) { + domains = append(domains, domain) } - return item -} - -// 检索静态路由规则 -func (s *Server) searchHandlerStatic(r *Request) *handlerCacheItem { - s.hmmu.RLock() - defer s.hmmu.RUnlock() - domains := []string{r.GetHost(), gDEFAULT_DOMAIN} - // 首先进行静态匹配 - for _, domain := range domains { - if f, ok := s.handlerMap[s.handlerKey(domain, r.Method, r.URL.Path)]; ok { - return &handlerCacheItem{f, nil} - } - } - return nil -} - -// 检索动态路由规则 -func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem { - s.hmmu.RLock() - defer s.hmmu.RUnlock() - domains := []string{gDEFAULT_DOMAIN, r.GetHost()} - array := strings.Split(r.URL.Path[1:], "/") + array := strings.Split(path[1:], "/") for _, domain := range domains { p, ok := s.handlerTree[domain] if !ok { @@ -356,17 +301,22 @@ func (s *Server) searchHandlerDynamic(r *Request) *handlerCacheItem { for e := lists[i].Front(); e != nil; e = e.Next() { item := e.Value.(*handlerRegisterItem) // 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储 - if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, r.Method) { - if match, err := gregex.MatchString(item.router.RegRule, r.URL.Path); err == nil && len(match) > 1 { + if strings.EqualFold(item.router.Method, gDEFAULT_METHOD) || strings.EqualFold(item.router.Method, method) { + if match, err := gregex.MatchString(item.router.RegRule, path); err == nil && len(match) > 1 { //gutil.Dump(match) //gutil.Dump(names) - handlerItem := &handlerCacheItem{item, nil} + handlerItem := &handlerParsedItem{item, nil} // 如果需要query匹配,那么需要重新正则解析URL if len(item.router.RegNames) > 0 { if len(match) > len(item.router.RegNames) { handlerItem.values = make(map[string][]string) - for index, name := range item.router.RegNames { - handlerItem.values[name] = []string{match[index + 1]} + // 如果存在存在同名路由参数名称,那么执行数组追加 + for i, name := range item.router.RegNames { + if _, ok := handlerItem.values[name]; ok { + handlerItem.values[name] = append(handlerItem.values[name], match[i + 1]) + } else { + handlerItem.values[name] = []string{match[i + 1]} + } } } } @@ -413,16 +363,8 @@ func (s *Server) patternToRegRule(rule string) (regrule string, names []string) return } -// 判断URI中是否包含动态注册规则 -func (s *Server) isPatternUriHasFuzzRule(uri string) bool { - if len(uri) > 1 && gregex.IsMatchString(`^/[:\*]|{[\w\.\-]+}`, uri) { - return true - } - return false -} - // 生成回调方法查询的Key -func (s *Server) handlerKey(domain, method, uri string) string { - return strings.ToUpper(method) + ":" + uri + "@" + strings.ToLower(domain) +func (s *Server) handlerKey(method, path, domain string) string { + return strings.ToUpper(method) + ":" + path + "@" + strings.ToLower(domain) } diff --git a/g/net/ghttp/ghttp_server_service.go b/g/net/ghttp/ghttp_server_service.go index d8dacb12d..115154f4c 100644 --- a/g/net/ghttp/ghttp_server_service.go +++ b/g/net/ghttp/ghttp_server_service.go @@ -17,7 +17,7 @@ import ( // 绑定URI到操作函数/方法 // pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn // 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error { +func (s *Server)bindHandlerItem(pattern string, item *handlerItem) error { if s.status == 1 { return errors.New("server handlers cannot be changed while running") } @@ -25,7 +25,7 @@ func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error { } // 通过映射数组绑定URI到操作函数/方法 -func (s *Server)bindHandlerByMap(m HandlerMap) error { +func (s *Server)bindHandlerByMap(m handlerMap) error { for p, h := range m { if err := s.bindHandlerItem(p, h); err != nil { return err @@ -73,7 +73,7 @@ func (s *Server) nameToUrlPart(name string) string { // 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑 func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { - return s.bindHandlerItem(pattern, &HandlerItem{ + return s.bindHandlerItem(pattern, &handlerItem{ ctype : nil, fname : "", faddr : handler, @@ -83,21 +83,21 @@ func (s *Server)BindHandler(pattern string, handler HandlerFunc) error { // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面 // 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义 func (s *Server)BindObject(pattern string, obj interface{}) error { - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(obj) t := v.Type() sname := t.Elem().Name() for i := 0; i < v.NumMethod(); i++ { method := t.Method(i).Name key := s.mergeBuildInNameToPattern(pattern, sname, method) - m[key] = &HandlerItem { + m[key] = &handlerItem { ctype : nil, fname : "", faddr : v.Method(i).Interface().(func(*Request)), } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI if strings.EqualFold(method, "Index") { - m[pattern] = &HandlerItem { + m[pattern] = &handlerItem { ctype : nil, fname : "", faddr : v.Method(i).Interface().(func(*Request)), @@ -110,7 +110,7 @@ func (s *Server)BindObject(pattern string, obj interface{}) error { // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面 // 第三个参数methods支持多个方法注册,多个方法以英文“,”号分隔,区分大小写 func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string) error { - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(obj) t := v.Type() sname := t.Elem().Name() @@ -121,14 +121,14 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string return errors.New("invalid method name:" + mname) } key := s.mergeBuildInNameToPattern(pattern, sname, mname) - m[key] = &HandlerItem{ + m[key] = &handlerItem{ ctype : nil, fname : "", faddr : fval.Interface().(func(*Request)), } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI if strings.EqualFold(mname, "Index") { - m[pattern] = &HandlerItem { + m[pattern] = &handlerItem { ctype : nil, fname : "", faddr : fval.Interface().(func(*Request)), @@ -141,7 +141,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面 // 需要注意对象方法的定义必须按照ghttp.HandlerFunc来定义 func (s *Server)BindObjectRest(pattern string, obj interface{}) error { - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(obj) t := v.Type() for i := 0; i < v.NumMethod(); i++ { @@ -151,7 +151,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { continue } key := name + ":" + pattern - m[key] = &HandlerItem { + m[key] = &handlerItem { ctype : nil, fname : "", faddr : v.Method(i).Interface().(func(*Request)), @@ -164,7 +164,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话 func (s *Server)BindController(pattern string, c Controller) error { // 遍历控制器,获取方法列表,并构造成uri - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(c) t := v.Type() sname := t.Elem().Name() @@ -174,14 +174,14 @@ func (s *Server)BindController(pattern string, c Controller) error { continue } key := s.mergeBuildInNameToPattern(pattern, sname, mname) - m[key] = &HandlerItem { + m[key] = &handlerItem { ctype : v.Elem().Type(), fname : mname, faddr : nil, } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI if strings.EqualFold(mname, "Index") { - m[pattern] = &HandlerItem { + m[pattern] = &handlerItem { ctype : v.Elem().Type(), fname : mname, faddr : nil, @@ -194,7 +194,7 @@ func (s *Server)BindController(pattern string, c Controller) error { // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话 // 第三个参数methods支持多个方法注册,多个方法以英文“,”号分隔,不区分大小写 func (s *Server)BindControllerMethod(pattern string, c Controller, methods string) error { - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(c) e := v.Type().Elem() t := v.Elem().Type() @@ -205,14 +205,14 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin return errors.New("invalid method name:" + mname) } key := s.mergeBuildInNameToPattern(pattern, sname, mname) - m[key] = &HandlerItem { + m[key] = &handlerItem { ctype : t, fname : mname, faddr : nil, } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI if strings.EqualFold(mname, "Index") { - m[pattern] = &HandlerItem { + m[pattern] = &handlerItem { ctype : t, fname : mname, faddr : nil, @@ -228,7 +228,7 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话 func (s *Server)BindControllerRest(pattern string, c Controller) error { // 遍历控制器,获取方法列表,并构造成uri - m := make(HandlerMap) + m := make(handlerMap) v := reflect.ValueOf(c) t := v.Type() // 如果存在与HttpMethod对应名字的方法,那么绑定这些方法 @@ -239,7 +239,7 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error { continue } key := name + ":" + pattern - m[key] = &HandlerItem { + m[key] = &handlerItem { ctype : v.Elem().Type(), fname : name, faddr : nil, diff --git a/geg/net/ghttp/server/events.go b/geg/net/ghttp/server/hooks/hooks1.go similarity index 84% rename from geg/net/ghttp/server/events.go rename to geg/net/ghttp/server/hooks/hooks1.go index 614fa6351..b4d426c0c 100644 --- a/geg/net/ghttp/server/events.go +++ b/geg/net/ghttp/server/hooks/hooks1.go @@ -6,7 +6,8 @@ import ( ) func main() { - p := "/" + // 基本事件回调使用 + p := "/:name/info/{uid}" s := ghttp.GetServer() s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{ "BeforeServe" : func(r *ghttp.Request){ fmt.Println("BeforeServe") }, @@ -17,7 +18,7 @@ func main() { "AfterClose" : func(r *ghttp.Request){ fmt.Println("AfterClose") }, }) s.BindHandler(p, func(r *ghttp.Request) { - r.Response.Write("哈喽世界!") + r.Response.Write("用户:", r.Get("name"), ", uid:", r.Get("uid")) }) s.SetPort(8199) s.Run() diff --git a/geg/net/ghttp/server/hooks/hooks2.go b/geg/net/ghttp/server/hooks/hooks2.go new file mode 100644 index 000000000..fff89307e --- /dev/null +++ b/geg/net/ghttp/server/hooks/hooks2.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "gitee.com/johng/gf/g/net/ghttp" +) + +func main() { + s := ghttp.GetServer() + + // 多事件回调示例,事件1 + p1 := "/:name/info/{uid}" + s.BindHookHandlerByMap(p1, map[string]ghttp.HandlerFunc { + "BeforeServe" : func(r *ghttp.Request){ + fmt.Println("打印到Server端终端") + }, + }) + s.BindHandler(p1, func(r *ghttp.Request) { + r.Response.Write("用户:", r.Get("name"), ", uid:", r.Get("uid")) + }) + + // 多事件回调示例,事件2 + p2 := "/{object}/list/{page}.java" + s.BindHookHandlerByMap(p2, map[string]ghttp.HandlerFunc{ + "BeforeOutput" : func(r *ghttp.Request){ + r.Response.SetBuffer([]byte( + fmt.Sprintf("通过事件修改输出内容, object: %s, page: %s", + r.Get("object"), r.GetRouterString("page"))), + ) + }, + }) + s.SetPort(8199) + s.Run() +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 13cb305f4..c960d6302 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,12 +1,9 @@ package main -import ( - "gitee.com/johng/gf/g/os/gfile" -) +import "fmt" func main() { - ghttp. - gfile.PutContentsAppend("/tmp/test", "1") - gfile.PutContentsAppend("/tmp/test", "2") - gfile.PutContentsAppend("/tmp/test", "3") + var a []int + a = append(a, 1) + fmt.Println(a) } \ No newline at end of file