From f3d859159da8b6efbdc054cb344885d6383a076c Mon Sep 17 00:00:00 2001 From: John Date: Tue, 17 Mar 2020 14:48:52 +0800 Subject: [PATCH] improve router group for duplicated router registering for package ghttp.Server --- encoding/gjson/gjson_z_unit_basic_test.go | 36 ----- encoding/gjson/gjson_z_unit_struct_test.go | 132 +++++++++++++++++++ frame/gmvc/model.go | 10 +- net/ghttp/ghttp_server.go | 3 +- net/ghttp/ghttp_server_domain.go | 127 +++++++++++------- net/ghttp/ghttp_server_router.go | 13 +- net/ghttp/ghttp_server_router_group.go | 126 +++++++++++------- net/ghttp/ghttp_server_router_hook.go | 5 + net/ghttp/ghttp_server_service_controller.go | 40 ++++-- net/ghttp/ghttp_server_service_handler.go | 8 +- net/ghttp/ghttp_server_service_object.go | 25 +++- net/ghttp/ghttp_unit_middleware_cors_test.go | 3 + 12 files changed, 370 insertions(+), 158 deletions(-) create mode 100644 encoding/gjson/gjson_z_unit_struct_test.go diff --git a/encoding/gjson/gjson_z_unit_basic_test.go b/encoding/gjson/gjson_z_unit_basic_test.go index ed62b7c39..7d89c2407 100644 --- a/encoding/gjson/gjson_z_unit_basic_test.go +++ b/encoding/gjson/gjson_z_unit_basic_test.go @@ -462,39 +462,3 @@ func Test_IsNil(t *testing.T) { gtest.Assert(j.IsNil(), true) }) } - -func Test_ToStructDeep(t *testing.T) { - gtest.Case(t, func() { - type Item struct { - Title string `json:"title"` - Key string `json:"key"` - } - - type M struct { - Id string `json:"id"` - Me map[string]interface{} `json:"me"` - Txt string `json:"txt"` - Items []*Item `json:"items"` - } - - txt := `{ - "id":"88888", - "me":{"name":"mikey","day":"20009"}, - "txt":"hello", - "items":null - }` - - j, err := gjson.LoadContent(txt) - gtest.Assert(err, nil) - gtest.Assert(j.GetString("me.name"), "mikey") - gtest.Assert(j.GetString("items"), "") - gtest.Assert(j.GetBool("items"), false) - gtest.Assert(j.GetArray("items"), nil) - m := new(M) - err = j.ToStructDeep(m) - gtest.Assert(err, nil) - gtest.AssertNE(m.Me, nil) - gtest.Assert(m.Me["day"], "20009") - gtest.Assert(m.Items, nil) - }) -} diff --git a/encoding/gjson/gjson_z_unit_struct_test.go b/encoding/gjson/gjson_z_unit_struct_test.go new file mode 100644 index 000000000..decc310ee --- /dev/null +++ b/encoding/gjson/gjson_z_unit_struct_test.go @@ -0,0 +1,132 @@ +// Copyright 2017 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 gjson_test + +import ( + "github.com/gogf/gf/encoding/gjson" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/test/gtest" + "testing" +) + +func Test_ToStruct1(t *testing.T) { + gtest.Case(t, func() { + type BaseInfoItem struct { + IdCardNumber string `db:"id_card_number" json:"idCardNumber" field:"id_card_number"` + IsHouseholder bool `db:"is_householder" json:"isHouseholder" field:"is_householder"` + HouseholderRelation string `db:"householder_relation" json:"householderRelation" field:"householder_relation"` + UserName string `db:"user_name" json:"userName" field:"user_name"` + UserSex string `db:"user_sex" json:"userSex" field:"user_sex"` + UserAge int `db:"user_age" json:"userAge" field:"user_age"` + UserNation string `db:"user_nation" json:"userNation" field:"user_nation"` + } + + type UserCollectionAddReq struct { + BaseInfo []BaseInfoItem `db:"_" json:"baseInfo" field:"_"` + } + jsonContent := `{ + "baseInfo": [{ + "idCardNumber": "520101199412141111", + "isHouseholder": true, + "householderRelation": "户主", + "userName": "李四", + "userSex": "男", + "userAge": 32, + "userNation": "苗族", + "userPhone": "13084183323", + "liveAddress": {}, + "occupationInfo": [{ + "occupationType": "经商", + "occupationBusinessInfo": [{ + "occupationClass": "制造业", + "businessLicenseNumber": "32020000012300", + "businessName": "土灶柴火鸡", + "spouseName": "", + "spouseIdCardNumber": "", + "businessLicensePhotoId": 125, + "businessPlace": "租赁房产", + "hasGoodsInsurance": true, + "businessScopeStr": "柴火鸡;烧烤", + "businessAddress": {}, + "businessPerformAbility": { + "businessType": "服务业", + "businessLife": 5, + "salesRevenue": 8000, + "familyEquity": 6000 + } + }], + "occupationWorkInfo": { + "occupationClass": "", + "companyName": "", + "companyType": "", + "workYearNum": 0, + "spouseName": "", + "spouseIdCardNumber": "", + "spousePhone": "", + "spouseEducation": "", + "spouseCompanyName": "", + "workLevel": "", + "workAddress": {}, + "workPerformAbility": { + "familyAnnualIncome": 0, + "familyEquity": 0, + "workCooperationState": "", + "workMoneyCooperationState": "" + } + }, + "occupationAgricultureInfo": [] + }], + "assetsInfo": [], + "expenditureInfo": [], + "incomeInfo": [], + "liabilityInfo": [] + }] +}` + data := new(UserCollectionAddReq) + j, err := gjson.LoadJson(jsonContent) + gtest.Assert(err, nil) + err = j.ToStruct(data) + gtest.Assert(err, nil) + g.Dump(data) + }) +} + +func Test_ToStructDeep(t *testing.T) { + gtest.Case(t, func() { + type Item struct { + Title string `json:"title"` + Key string `json:"key"` + } + + type M struct { + Id string `json:"id"` + Me map[string]interface{} `json:"me"` + Txt string `json:"txt"` + Items []*Item `json:"items"` + } + + txt := `{ + "id":"88888", + "me":{"name":"mikey","day":"20009"}, + "txt":"hello", + "items":null + }` + + j, err := gjson.LoadContent(txt) + gtest.Assert(err, nil) + gtest.Assert(j.GetString("me.name"), "mikey") + gtest.Assert(j.GetString("items"), "") + gtest.Assert(j.GetBool("items"), false) + gtest.Assert(j.GetArray("items"), nil) + m := new(M) + err = j.ToStructDeep(m) + gtest.Assert(err, nil) + gtest.AssertNE(m.Me, nil) + gtest.Assert(m.Me["day"], "20009") + gtest.Assert(m.Items, nil) + }) +} diff --git a/frame/gmvc/model.go b/frame/gmvc/model.go index 81a0ea2a5..5eb656d0d 100644 --- a/frame/gmvc/model.go +++ b/frame/gmvc/model.go @@ -8,9 +8,7 @@ package gmvc import "github.com/gogf/gf/database/gdb" -// M is alias for Model, -// just for short write purpose. -type M = Model - -// Model is alias for *gdb.Model. -type Model = *gdb.Model +type ( + M = Model // M is alias for Model, just for short write purpose. + Model = *gdb.Model // Model is alias for *gdb.Model. +) diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index b446a17e8..a9e219b09 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -89,6 +89,7 @@ type ( ctrlInfo *handlerController // Controller information for reflect usage. hookName string // Hook type name. router *Router // Router object. + source string // Source file path:line when registering. } // handlerParsedItem is the item parsed from URL.Path. @@ -105,7 +106,7 @@ type ( // registeredRouteItem stores the information of the router and is used for route map. registeredRouteItem struct { - file string // Source file path and its line number. + source string // Source file path and its line number. handler *handlerItem // Handler object. } diff --git a/net/ghttp/ghttp_server_domain.go b/net/ghttp/ghttp_server_domain.go index a7a8c8564..25faf9ea0 100644 --- a/net/ghttp/ghttp_server_domain.go +++ b/net/ghttp/ghttp_server_domain.go @@ -12,121 +12,148 @@ import ( // 域名管理器对象 type Domain struct { - s *Server // 所属Server - m map[string]bool // 多域名 + server *Server // 所属Server + domains map[string]struct{} // 多域名 } // 生成一个域名对象, 参数 domains 支持给定多个域名。 func (s *Server) Domain(domains string) *Domain { d := &Domain{ - s: s, - m: make(map[string]bool), + server: s, + domains: make(map[string]struct{}), } for _, v := range strings.Split(domains, ",") { - d.m[strings.TrimSpace(v)] = true + d.domains[strings.TrimSpace(v)] = struct{}{} } return d } func (d *Domain) BindHandler(pattern string, handler HandlerFunc) { - for domain, _ := range d.m { - d.s.BindHandler(pattern+"@"+domain, handler) + for domain, _ := range d.domains { + d.server.BindHandler(pattern+"@"+domain, handler) } } -func (d *Domain) doBindHandler(pattern string, handler HandlerFunc, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindHandler(pattern+"@"+domain, handler, middleware) +func (d *Domain) doBindHandler( + pattern string, handler HandlerFunc, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindHandler(pattern+"@"+domain, handler, middleware, source) } } func (d *Domain) BindObject(pattern string, obj interface{}, methods ...string) { - for domain, _ := range d.m { - d.s.BindObject(pattern+"@"+domain, obj, methods...) + for domain, _ := range d.domains { + d.server.BindObject(pattern+"@"+domain, obj, methods...) } } -func (d *Domain) doBindObject(pattern string, obj interface{}, methods string, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindObject(pattern+"@"+domain, obj, methods, middleware) +func (d *Domain) doBindObject( + pattern string, obj interface{}, methods string, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindObject(pattern+"@"+domain, obj, methods, middleware, source) } } func (d *Domain) BindObjectMethod(pattern string, obj interface{}, method string) { - for domain, _ := range d.m { - d.s.BindObjectMethod(pattern+"@"+domain, obj, method) + for domain, _ := range d.domains { + d.server.BindObjectMethod(pattern+"@"+domain, obj, method) } } -func (d *Domain) doBindObjectMethod(pattern string, obj interface{}, method string, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindObjectMethod(pattern+"@"+domain, obj, method, middleware) +func (d *Domain) doBindObjectMethod( + pattern string, obj interface{}, method string, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindObjectMethod(pattern+"@"+domain, obj, method, middleware, source) } } func (d *Domain) BindObjectRest(pattern string, obj interface{}) { - for domain, _ := range d.m { - d.s.BindObjectRest(pattern+"@"+domain, obj) + for domain, _ := range d.domains { + d.server.BindObjectRest(pattern+"@"+domain, obj) } } -func (d *Domain) doBindObjectRest(pattern string, obj interface{}, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindObjectRest(pattern+"@"+domain, obj, middleware) +func (d *Domain) doBindObjectRest( + pattern string, obj interface{}, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindObjectRest(pattern+"@"+domain, obj, middleware, source) } } func (d *Domain) BindController(pattern string, c Controller, methods ...string) { - for domain, _ := range d.m { - d.s.BindController(pattern+"@"+domain, c, methods...) + for domain, _ := range d.domains { + d.server.BindController(pattern+"@"+domain, c, methods...) } } -func (d *Domain) doBindController(pattern string, c Controller, methods string, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindController(pattern+"@"+domain, c, methods, middleware) +func (d *Domain) doBindController( + pattern string, c Controller, methods string, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindController(pattern+"@"+domain, c, methods, middleware, source) } } func (d *Domain) BindControllerMethod(pattern string, c Controller, method string) { - for domain, _ := range d.m { - d.s.BindControllerMethod(pattern+"@"+domain, c, method) + for domain, _ := range d.domains { + d.server.BindControllerMethod(pattern+"@"+domain, c, method) } } -func (d *Domain) doBindControllerMethod(pattern string, c Controller, method string, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindControllerMethod(pattern+"@"+domain, c, method, middleware) +func (d *Domain) doBindControllerMethod( + pattern string, c Controller, method string, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindControllerMethod(pattern+"@"+domain, c, method, middleware, source) } } func (d *Domain) BindControllerRest(pattern string, c Controller) { - for domain, _ := range d.m { - d.s.BindControllerRest(pattern+"@"+domain, c) + for domain, _ := range d.domains { + d.server.BindControllerRest(pattern+"@"+domain, c) } } -func (d *Domain) doBindControllerRest(pattern string, c Controller, middleware []HandlerFunc) { - for domain, _ := range d.m { - d.s.doBindControllerRest(pattern+"@"+domain, c, middleware) +func (d *Domain) doBindControllerRest( + pattern string, c Controller, + middleware []HandlerFunc, source string, +) { + for domain, _ := range d.domains { + d.server.doBindControllerRest(pattern+"@"+domain, c, middleware, source) } } func (d *Domain) BindHookHandler(pattern string, hook string, handler HandlerFunc) { - for domain, _ := range d.m { - d.s.BindHookHandler(pattern+"@"+domain, hook, handler) + for domain, _ := range d.domains { + d.server.BindHookHandler(pattern+"@"+domain, hook, handler) + } +} + +func (d *Domain) doBindHookHandler(pattern string, hook string, handler HandlerFunc, source string) { + for domain, _ := range d.domains { + d.server.doBindHookHandler(pattern+"@"+domain, hook, handler, source) } } func (d *Domain) BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) { - for domain, _ := range d.m { - d.s.BindHookHandlerByMap(pattern+"@"+domain, hookmap) + for domain, _ := range d.domains { + d.server.BindHookHandlerByMap(pattern+"@"+domain, hookmap) } } func (d *Domain) BindStatusHandler(status int, handler HandlerFunc) { - for domain, _ := range d.m { - d.s.setStatusHandler(d.s.statusHandlerKey(status, domain), handler) + for domain, _ := range d.domains { + d.server.setStatusHandler(d.server.statusHandlerKey(status, domain), handler) } } @@ -137,14 +164,14 @@ func (d *Domain) BindStatusHandlerByMap(handlerMap map[int]HandlerFunc) { } func (d *Domain) BindMiddleware(pattern string, handlers ...HandlerFunc) { - for domain, _ := range d.m { - d.s.BindMiddleware(pattern+"@"+domain, handlers...) + for domain, _ := range d.domains { + d.server.BindMiddleware(pattern+"@"+domain, handlers...) } } func (d *Domain) BindMiddlewareDefault(handlers ...HandlerFunc) { - for domain, _ := range d.m { - d.s.BindMiddleware(gDEFAULT_MIDDLEWARE_PATTERN+"@"+domain, handlers...) + for domain, _ := range d.domains { + d.server.BindMiddleware(gDEFAULT_MIDDLEWARE_PATTERN+"@"+domain, handlers...) } } diff --git a/net/ghttp/ghttp_server_router.go b/net/ghttp/ghttp_server_router.go index e27767893..00ff298f9 100644 --- a/net/ghttp/ghttp_server_router.go +++ b/net/ghttp/ghttp_server_router.go @@ -67,6 +67,10 @@ func (s *Server) parsePattern(pattern string) (domain, method, path string, err // is the well designed router storage structure for router searching when the request is under serving. func (s *Server) setHandler(pattern string, handler *handlerItem) { handler.itemId = handlerIdGenerator.Add(1) + if handler.source == "" { + _, file, line := gdebug.CallerWithFilter(gFILTER_KEY) + handler.source = fmt.Sprintf(`%s:%d`, file, line) + } domain, method, uri, err := s.parsePattern(pattern) if err != nil { s.Logger().Fatal("invalid pattern:", pattern, err) @@ -83,7 +87,10 @@ func (s *Server) setHandler(pattern string, handler *handlerItem) { switch handler.itemType { case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER: if item, ok := s.routesMap[routerKey]; ok { - s.Logger().Fatalf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file) + s.Logger().Fatalf( + `duplicated route registry "%s" at %s , already registered at %s`, + pattern, handler.source, item[0].source, + ) return } } @@ -184,9 +191,9 @@ func (s *Server) setHandler(pattern string, handler *handlerItem) { if _, ok := s.routesMap[routerKey]; !ok { s.routesMap[routerKey] = make([]registeredRouteItem, 0) } - _, file, line := gdebug.CallerWithFilter(gFILTER_KEY) + routeItem := registeredRouteItem{ - file: fmt.Sprintf(`%s:%d`, file, line), + source: handler.source, handler: handler, } switch handler.itemType { diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 64915eedd..53480e5e6 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -7,6 +7,8 @@ package ghttp import ( + "fmt" + "github.com/gogf/gf/debug/gdebug" "reflect" "strings" @@ -36,6 +38,7 @@ type ( pattern string object interface{} // Can be handler, controller or object. params []interface{} // Extra parameters for route registering depending on the type. + source string // // Handler is register at certain source file path:line. } ) @@ -53,10 +56,10 @@ func (s *Server) handlePreBindItems() { if item.group.server != nil && item.group.server != s { continue } - if item.group.domain != nil && item.group.domain.s != s { + if item.group.domain != nil && item.group.domain.server != s { continue } - item.group.doBind(item.bindType, item.pattern, item.object, item.params...) + item.group.doBindRoutersToServer(item) } preBindItems = preBindItems[:0] } @@ -147,9 +150,9 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { bindType := gstr.ToUpper(gconv.String(item[0])) switch bindType { case "REST": - group.preBind("REST", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + group.preBindToLocalArray("REST", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) case "MIDDLEWARE": - group.preBind("MIDDLEWARE", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) + group.preBindToLocalArray("MIDDLEWARE", gconv.String(item[0])+":"+gconv.String(item[1]), item[2]) default: if strings.EqualFold(bindType, "ALL") { bindType = "" @@ -157,9 +160,9 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { bindType += ":" } if len(item) > 3 { - group.preBind("HANDLER", bindType+gconv.String(item[1]), item[2], item[3]) + group.preBindToLocalArray("HANDLER", bindType+gconv.String(item[1]), item[2], item[3]) } else { - group.preBind("HANDLER", bindType+gconv.String(item[1]), item[2]) + group.preBindToLocalArray("HANDLER", bindType+gconv.String(item[1]), item[2]) } } } @@ -168,62 +171,62 @@ func (g *RouterGroup) Bind(items []GroupItem) *RouterGroup { // ALL registers a http handler to given route pattern and all http methods. func (g *RouterGroup) ALL(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", gDEFAULT_METHOD+":"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", gDEFAULT_METHOD+":"+pattern, object, params...) } // GET registers a http handler to given route pattern and http method: GET. func (g *RouterGroup) GET(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "GET:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "GET:"+pattern, object, params...) } // PUT registers a http handler to given route pattern and http method: PUT. func (g *RouterGroup) PUT(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "PUT:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "PUT:"+pattern, object, params...) } // POST registers a http handler to given route pattern and http method: POST. func (g *RouterGroup) POST(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "POST:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "POST:"+pattern, object, params...) } // DELETE registers a http handler to given route pattern and http method: DELETE. func (g *RouterGroup) DELETE(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "DELETE:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "DELETE:"+pattern, object, params...) } // PATCH registers a http handler to given route pattern and http method: PATCH. func (g *RouterGroup) PATCH(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "PATCH:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "PATCH:"+pattern, object, params...) } // HEAD registers a http handler to given route pattern and http method: HEAD. func (g *RouterGroup) HEAD(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "HEAD:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "HEAD:"+pattern, object, params...) } // CONNECT registers a http handler to given route pattern and http method: CONNECT. func (g *RouterGroup) CONNECT(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "CONNECT:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "CONNECT:"+pattern, object, params...) } // OPTIONS registers a http handler to given route pattern and http method: OPTIONS. func (g *RouterGroup) OPTIONS(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "OPTIONS:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "OPTIONS:"+pattern, object, params...) } // TRACE registers a http handler to given route pattern and http method: TRACE. func (g *RouterGroup) TRACE(pattern string, object interface{}, params ...interface{}) *RouterGroup { - return g.Clone().preBind("HANDLER", "TRACE:"+pattern, object, params...) + return g.Clone().preBindToLocalArray("HANDLER", "TRACE:"+pattern, object, params...) } // REST registers a http handler to given route pattern according to REST rule. func (g *RouterGroup) REST(pattern string, object interface{}) *RouterGroup { - return g.Clone().preBind("REST", pattern, object) + return g.Clone().preBindToLocalArray("REST", pattern, object) } // Hook registers a hook to given route pattern. func (g *RouterGroup) Hook(pattern string, hook string, handler HandlerFunc) *RouterGroup { - return g.Clone().preBind("HANDLER", pattern, handler, hook) + return g.Clone().preBindToLocalArray("HANDLER", pattern, handler, hook) } // Middleware binds one or more middleware to the router group. @@ -232,14 +235,16 @@ func (g *RouterGroup) Middleware(handlers ...HandlerFunc) *RouterGroup { return g } -// preBind adds the route registering parameters to internal variable array for lazily registering feature. -func (g *RouterGroup) preBind(bindType string, pattern string, object interface{}, params ...interface{}) *RouterGroup { +// preBindToLocalArray adds the route registering parameters to internal variable array for lazily registering feature. +func (g *RouterGroup) preBindToLocalArray(bindType string, pattern string, object interface{}, params ...interface{}) *RouterGroup { + _, file, line := gdebug.CallerWithFilter(gFILTER_KEY) preBindItems = append(preBindItems, preBindItem{ group: g, bindType: bindType, pattern: pattern, object: object, params: params, + source: fmt.Sprintf(`%s:%d`, file, line), }) return g } @@ -255,8 +260,15 @@ func (g *RouterGroup) getPrefix() string { return prefix } -// doBind does really registering for the group. -func (g *RouterGroup) doBind(bindType string, pattern string, object interface{}, params ...interface{}) *RouterGroup { +// doBindRoutersToServer does really registering for the group. +func (g *RouterGroup) doBindRoutersToServer(item preBindItem) *RouterGroup { + var ( + bindType = item.bindType + pattern = item.pattern + object = item.object + params = item.params + source = item.source + ) prefix := g.getPrefix() // Route check. if len(prefix) > 0 { @@ -271,7 +283,9 @@ func (g *RouterGroup) doBind(bindType string, pattern string, object interface{} if bindType == "REST" { pattern = prefix + "/" + strings.TrimLeft(path, "/") } else { - pattern = g.server.serveHandlerKey(method, prefix+"/"+strings.TrimLeft(path, "/"), domain) + pattern = g.server.serveHandlerKey( + method, prefix+"/"+strings.TrimLeft(path, "/"), domain, + ) } } // Filter repeated char '/'. @@ -286,78 +300,102 @@ func (g *RouterGroup) doBind(bindType string, pattern string, object interface{} case "HANDLER": if h, ok := object.(HandlerFunc); ok { if g.server != nil { - g.server.doBindHandler(pattern, h, g.middleware) + g.server.doBindHandler(pattern, h, g.middleware, source) } else { - g.domain.doBindHandler(pattern, h, g.middleware) + g.domain.doBindHandler(pattern, h, g.middleware, source) } } else if g.isController(object) { if len(extras) > 0 { if g.server != nil { if gstr.Contains(extras[0], ",") { - g.server.doBindController(pattern, object.(Controller), extras[0], g.middleware) + g.server.doBindController( + pattern, object.(Controller), extras[0], g.middleware, source, + ) } else { - g.server.doBindControllerMethod(pattern, object.(Controller), extras[0], g.middleware) + g.server.doBindControllerMethod( + pattern, object.(Controller), extras[0], g.middleware, source, + ) } } else { if gstr.Contains(extras[0], ",") { - g.domain.doBindController(pattern, object.(Controller), extras[0], g.middleware) + g.domain.doBindController( + pattern, object.(Controller), extras[0], g.middleware, source, + ) } else { - g.domain.doBindControllerMethod(pattern, object.(Controller), extras[0], g.middleware) + g.domain.doBindControllerMethod( + pattern, object.(Controller), extras[0], g.middleware, source, + ) } } } else { if g.server != nil { - g.server.doBindController(pattern, object.(Controller), "", g.middleware) + g.server.doBindController( + pattern, object.(Controller), "", g.middleware, source, + ) } else { - g.domain.doBindController(pattern, object.(Controller), "", g.middleware) + g.domain.doBindController( + pattern, object.(Controller), "", g.middleware, source, + ) } } } else { if len(extras) > 0 { if g.server != nil { if gstr.Contains(extras[0], ",") { - g.server.doBindObject(pattern, object, extras[0], g.middleware) + g.server.doBindObject( + pattern, object, extras[0], g.middleware, source, + ) } else { - g.server.doBindObjectMethod(pattern, object, extras[0], g.middleware) + g.server.doBindObjectMethod( + pattern, object, extras[0], g.middleware, source, + ) } } else { if gstr.Contains(extras[0], ",") { - g.domain.doBindObject(pattern, object, extras[0], g.middleware) + g.domain.doBindObject( + pattern, object, extras[0], g.middleware, source, + ) } else { - g.domain.doBindObjectMethod(pattern, object, extras[0], g.middleware) + g.domain.doBindObjectMethod( + pattern, object, extras[0], g.middleware, source, + ) } } } else { if g.server != nil { - g.server.doBindObject(pattern, object, "", g.middleware) + g.server.doBindObject(pattern, object, "", g.middleware, source) } else { - g.domain.doBindObject(pattern, object, "", g.middleware) + g.domain.doBindObject(pattern, object, "", g.middleware, source) } } } case "REST": if g.isController(object) { if g.server != nil { - g.server.doBindControllerRest(pattern, object.(Controller), g.middleware) + g.server.doBindControllerRest( + pattern, object.(Controller), g.middleware, source, + ) } else { - g.domain.doBindControllerRest(pattern, object.(Controller), g.middleware) + g.domain.doBindControllerRest( + pattern, object.(Controller), g.middleware, source, + ) } } else { if g.server != nil { - g.server.doBindObjectRest(pattern, object, g.middleware) + g.server.doBindObjectRest(pattern, object, g.middleware, source) } else { - g.domain.doBindObjectRest(pattern, object, g.middleware) + g.domain.doBindObjectRest(pattern, object, g.middleware, source) } } case "HOOK": if h, ok := object.(HandlerFunc); ok { if g.server != nil { - g.server.BindHookHandler(pattern, extras[0], h) + g.server.doBindHookHandler(pattern, extras[0], h, source) } else { - g.domain.BindHookHandler(pattern, extras[0], h) + g.domain.doBindHookHandler(pattern, extras[0], h, source) } } else { - g.server.Logger().Fatalf("invalid hook handler for pattern:%s", pattern) + g.server.Logger().Fatalf("invalid hook handler for pattern: %s", pattern) } } return g diff --git a/net/ghttp/ghttp_server_router_hook.go b/net/ghttp/ghttp_server_router_hook.go index 63d170dba..e918c6e30 100644 --- a/net/ghttp/ghttp_server_router_hook.go +++ b/net/ghttp/ghttp_server_router_hook.go @@ -13,11 +13,16 @@ import ( // 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写 func (s *Server) BindHookHandler(pattern string, hook string, handler HandlerFunc) { + s.doBindHookHandler(pattern, hook, handler, "") +} + +func (s *Server) doBindHookHandler(pattern string, hook string, handler HandlerFunc, source string) { s.setHandler(pattern, &handlerItem{ itemType: gHANDLER_TYPE_HOOK, itemName: gdebug.FuncPath(handler), itemFunc: handler, hookName: hook, + source: source, }) } diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go index ef6f4c8c6..e86f14cad 100644 --- a/net/ghttp/ghttp_server_service_controller.go +++ b/net/ghttp/ghttp_server_service_controller.go @@ -24,12 +24,12 @@ func (s *Server) BindController(pattern string, controller Controller, method .. if len(method) > 0 { bindMethod = method[0] } - s.doBindController(pattern, controller, bindMethod, nil) + s.doBindController(pattern, controller, bindMethod, nil, "") } // 绑定路由到指定的方法执行, 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。 func (s *Server) BindControllerMethod(pattern string, controller Controller, method string) { - s.doBindControllerMethod(pattern, controller, method, nil) + s.doBindControllerMethod(pattern, controller, method, nil, "") } // 绑定控制器(RESTFul),控制器需要实现gmvc.Controller接口 @@ -37,10 +37,13 @@ func (s *Server) BindControllerMethod(pattern string, controller Controller, met // 因此只会绑定HTTP Method对应的方法,其他方法不会自动注册绑定 // 这种方式绑定的控制器每一次请求都会初始化一个新的控制器对象进行处理,对应不同的请求会话 func (s *Server) BindControllerRest(pattern string, controller Controller) { - s.doBindControllerRest(pattern, controller, nil) + s.doBindControllerRest(pattern, controller, nil, "") } -func (s *Server) doBindController(pattern string, controller Controller, method string, middleware []HandlerFunc) { +func (s *Server) doBindController( + pattern string, controller Controller, method string, + middleware []HandlerFunc, source string, +) { // Convert input method to map for convenience and high performance searching. var methodMap map[string]bool if len(method) > 0 { @@ -98,6 +101,7 @@ func (s *Server) doBindController(pattern string, controller Controller, method reflect: v.Elem().Type(), }, middleware: middleware, + source: source, } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI, // 例如: pattern为/user, 那么会同时注册/user及/user/index, @@ -117,13 +121,20 @@ func (s *Server) doBindController(pattern string, controller Controller, method reflect: v.Elem().Type(), }, middleware: middleware, + source: source, } } } s.bindHandlerByMap(m) } -func (s *Server) doBindControllerMethod(pattern string, controller Controller, method string, middleware []HandlerFunc) { +func (s *Server) doBindControllerMethod( + pattern string, + controller Controller, + method string, + middleware []HandlerFunc, + source string, +) { m := make(map[string]*handlerItem) v := reflect.ValueOf(controller) t := v.Type() @@ -141,8 +152,10 @@ func (s *Server) doBindControllerMethod(pattern string, controller Controller, m ctlName = fmt.Sprintf(`(%s)`, ctlName) } if _, ok := methodValue.Interface().(func()); !ok { - s.Logger().Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, - pkgPath, ctlName, methodName, methodValue.Type().String()) + s.Logger().Errorf( + `invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, + pkgPath, ctlName, methodName, methodValue.Type().String(), + ) return } key := s.mergeBuildInNameToPattern(pattern, structName, methodName, false) @@ -154,11 +167,15 @@ func (s *Server) doBindControllerMethod(pattern string, controller Controller, m reflect: v.Elem().Type(), }, middleware: middleware, + source: source, } s.bindHandlerByMap(m) } -func (s *Server) doBindControllerRest(pattern string, controller Controller, middleware []HandlerFunc) { +func (s *Server) doBindControllerRest( + pattern string, controller Controller, + middleware []HandlerFunc, source string, +) { // 遍历控制器,获取方法列表,并构造成uri m := make(map[string]*handlerItem) v := reflect.ValueOf(controller) @@ -177,8 +194,10 @@ func (s *Server) doBindControllerRest(pattern string, controller Controller, mid ctlName = fmt.Sprintf(`(%s)`, ctlName) } if _, ok := v.Method(i).Interface().(func()); !ok { - s.Logger().Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, - pkgPath, ctlName, methodName, v.Method(i).Type().String()) + s.Logger().Errorf( + `invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`, + pkgPath, ctlName, methodName, v.Method(i).Type().String(), + ) return } key := s.mergeBuildInNameToPattern(methodName+":"+pattern, structName, methodName, false) @@ -190,6 +209,7 @@ func (s *Server) doBindControllerRest(pattern string, controller Controller, mid reflect: v.Elem().Type(), }, middleware: middleware, + source: source, } } s.bindHandlerByMap(m) diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index a53807bce..7a1c8750d 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -16,18 +16,22 @@ import ( // 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑 func (s *Server) BindHandler(pattern string, handler HandlerFunc) { - s.doBindHandler(pattern, handler, nil) + s.doBindHandler(pattern, handler, nil, "") } // 绑定URI到操作函数/方法 // pattern的格式形如:/user/list, put:/user, delete:/user, post:/user@johng.cn // 支持RESTful的请求格式,具体业务逻辑由绑定的处理方法来执行 -func (s *Server) doBindHandler(pattern string, handler HandlerFunc, middleware []HandlerFunc) { +func (s *Server) doBindHandler( + pattern string, handler HandlerFunc, + middleware []HandlerFunc, source string, +) { s.setHandler(pattern, &handlerItem{ itemName: gdebug.FuncPath(handler), itemType: gHANDLER_TYPE_HANDLER, itemFunc: handler, middleware: middleware, + source: source, }) } diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index 4fd135822..d6a167cdb 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -23,22 +23,25 @@ func (s *Server) BindObject(pattern string, object interface{}, method ...string if len(method) > 0 { bindMethod = method[0] } - s.doBindObject(pattern, object, bindMethod, nil) + s.doBindObject(pattern, object, bindMethod, nil, "") } // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面, // 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。 func (s *Server) BindObjectMethod(pattern string, object interface{}, method string) { - s.doBindObjectMethod(pattern, object, method, nil) + s.doBindObjectMethod(pattern, object, method, nil, "") } // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面, // 需要注意对象方法的定义必须按照 ghttp.HandlerFunc 来定义 func (s *Server) BindObjectRest(pattern string, object interface{}) { - s.doBindObjectRest(pattern, object, nil) + s.doBindObjectRest(pattern, object, nil, "") } -func (s *Server) doBindObject(pattern string, object interface{}, method string, middleware []HandlerFunc) { +func (s *Server) doBindObject( + pattern string, object interface{}, method string, + middleware []HandlerFunc, source string, +) { // Convert input method to map for convenience and high performance searching purpose. var methodMap map[string]bool if len(method) > 0 { @@ -107,6 +110,7 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, + source: source, } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI。 // 注意,当pattern带有内置变量时,不会自动加该路由。 @@ -123,6 +127,7 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, + source: source, } } } @@ -131,7 +136,10 @@ func (s *Server) doBindObject(pattern string, object interface{}, method string, // 绑定对象到URI请求处理中,会自动识别方法名称,并附加到对应的URI地址后面, // 第三个参数method仅支持一个方法注册,不支持多个,并且区分大小写。 -func (s *Server) doBindObjectMethod(pattern string, object interface{}, method string, middleware []HandlerFunc) { +func (s *Server) doBindObjectMethod( + pattern string, object interface{}, method string, + middleware []HandlerFunc, source string, +) { m := make(map[string]*handlerItem) v := reflect.ValueOf(object) t := v.Type() @@ -170,12 +178,16 @@ func (s *Server) doBindObjectMethod(pattern string, object interface{}, method s initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, + source: source, } s.bindHandlerByMap(m) } -func (s *Server) doBindObjectRest(pattern string, object interface{}, middleware []HandlerFunc) { +func (s *Server) doBindObjectRest( + pattern string, object interface{}, + middleware []HandlerFunc, source string, +) { m := make(map[string]*handlerItem) v := reflect.ValueOf(object) t := v.Type() @@ -213,6 +225,7 @@ func (s *Server) doBindObjectRest(pattern string, object interface{}, middleware initFunc: initFunc, shutFunc: shutFunc, middleware: middleware, + source: source, } } s.bindHandlerByMap(m) diff --git a/net/ghttp/ghttp_unit_middleware_cors_test.go b/net/ghttp/ghttp_unit_middleware_cors_test.go index c99e16c97..4044d59e6 100644 --- a/net/ghttp/ghttp_unit_middleware_cors_test.go +++ b/net/ghttp/ghttp_unit_middleware_cors_test.go @@ -40,6 +40,7 @@ func Test_Middleware_CORS(t *testing.T) { resp, err := client.Get("/api.v2/user/list") gtest.Assert(err, nil) gtest.Assert(len(resp.Header["Access-Control-Allow-Headers"]), 0) + gtest.Assert(resp.StatusCode, 404) resp.Close() // POST request matches the route and CORS middleware. @@ -61,6 +62,7 @@ func Test_Middleware_CORS(t *testing.T) { gtest.Assert(err, nil) gtest.Assert(len(resp.Header["Access-Control-Allow-Headers"]), 0) gtest.Assert(resp.ReadAllString(), "Not Found") + gtest.Assert(resp.StatusCode, 404) resp.Close() }) // OPTIONS POST @@ -71,6 +73,7 @@ func Test_Middleware_CORS(t *testing.T) { resp, err := client.Options("/api.v2/user/list") gtest.Assert(err, nil) gtest.Assert(len(resp.Header["Access-Control-Allow-Headers"]), 1) + gtest.Assert(resp.StatusCode, 200) resp.Close() }) }