diff --git a/TODO.MD b/TODO.MD index 8d5d553cb..01a25882e 100644 --- a/TODO.MD +++ b/TODO.MD @@ -53,7 +53,7 @@ 1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收; 1. gdb的Data方法支持struct参数传入; 1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap; - +1. 增加对ghttp路由注册的{.struct}/{.method}单元测试; diff --git a/g/net/ghttp/ghttp_response.go b/g/net/ghttp/ghttp_response.go index 163cabccb..f752971a2 100644 --- a/g/net/ghttp/ghttp_response.go +++ b/g/net/ghttp/ghttp_response.go @@ -118,15 +118,17 @@ func (r *Response) WriteXml(content interface{}, rootTag...string) error { return nil } -// 允许AJAX跨域访问 +// Deprecated, please use CORSDefault instead. +// +// (已废弃,请使用CORSDefault)允许AJAX跨域访问. func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods string, maxAge...int) { age := 3628800 if len(maxAge) > 0 { age = maxAge[0] } - r.Header().Set("Access-Control-Allow-Origin", allowOrigin); - r.Header().Set("Access-Control-Allow-Methods", allowMethods); - r.Header().Set("Access-Control-Max-Age", strconv.Itoa(age)); + r.Header().Set("Access-Control-Allow-Origin", allowOrigin) + r.Header().Set("Access-Control-Allow-Methods", allowMethods) + r.Header().Set("Access-Control-Max-Age", strconv.Itoa(age)) } // 返回HTTP Code状态码 diff --git a/g/net/ghttp/ghttp_response_cors.go b/g/net/ghttp/ghttp_response_cors.go new file mode 100644 index 000000000..99bb128ea --- /dev/null +++ b/g/net/ghttp/ghttp_response_cors.go @@ -0,0 +1,62 @@ +// Copyright 2019 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 + +import ( + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" +) + +// See https://www.w3.org/TR/cors/ . +// 服务端允许跨域请求选项 +type CORSOptions struct { + AllowOrigin string // Access-Control-Allow-Origin + AllowCredentials string // Access-Control-Allow-Credentials + ExposeHeaders string // Access-Control-Expose-Headers + MaxAge int // Access-Control-Max-Age + AllowMethods string // Access-Control-Allow-Methods + AllowHeaders string // Access-Control-Allow-Headers +} + +// 默认的CORS配置 +func (r *Response) DefaultCORSOptions() CORSOptions { + return CORSOptions { + AllowOrigin : gstr.TrimRight(r.request.Referer(), "/"), + AllowMethods : HTTP_METHODS, + AllowCredentials : "true", + MaxAge : 3628800, + } +} + +// See https://www.w3.org/TR/cors/ . +// 允许请求跨域访问. +func (r *Response) CORS(options CORSOptions) { + if options.AllowOrigin != "" { + r.Header().Set("Access-Control-Allow-Origin", options.AllowOrigin) + } + if options.AllowCredentials != "" { + r.Header().Set("Access-Control-Allow-Credentials", options.AllowCredentials) + } + if options.ExposeHeaders != "" { + r.Header().Set("Access-Control-Expose-Headers", options.ExposeHeaders) + } + if options.MaxAge != 0 { + r.Header().Set("Access-Control-Max-Age", gconv.String(options.MaxAge)) + } + if options.AllowMethods != "" { + r.Header().Set("Access-Control-Allow-Methods", options.AllowMethods) + } + if options.AllowHeaders != "" { + r.Header().Set("Access-Control-Allow-Headers", options.AllowHeaders) + } +} + +// 允许请求跨域访问(使用more配置). +func (r *Response) CORSDefault() { + r.CORS(r.DefaultCORSOptions()) +} diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 22a1123e3..c223ded74 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -41,7 +41,6 @@ type ( servers []*gracefulServer // 底层http.Server列表 serverCount *gtype.Int // 底层http.Server数量 closeChan chan struct{} // 用以关闭事件通知的通道 - methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充) servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID // 服务注册相关 serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配) @@ -112,7 +111,7 @@ const ( HOOK_BEFORE_CLOSE = "BeforeClose" HOOK_AFTER_CLOSE = "AfterClose" - gHTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" + HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE" gDEFAULT_SERVER = "default" gDEFAULT_DOMAIN = "default" gDEFAULT_METHOD = "ALL" @@ -125,6 +124,10 @@ const ( ) var ( + // 所有支持的HTTP Method Map(初始化时自动填充), + // 用于快速检索需要 + methodsMap = make(map[string]struct{}) + // WebServer表,用以存储和检索名称与Server对象之间的关联关系 serverMapping = gmap.NewStringInterfaceMap() @@ -148,6 +151,12 @@ var ( gracefulEnabled = true ) +func init() { + for _, v := range strings.Split(HTTP_METHODS, ",") { + methodsMap[v] = struct{}{} + } +} + // 是否开启平滑重启特性 func SetGraceful(enabled bool) { gracefulEnabled = enabled @@ -198,7 +207,6 @@ func GetServer(name...interface{}) (*Server) { servers : make([]*gracefulServer, 0), closeChan : make(chan struct{}, 100), serverCount : gtype.NewInt(), - methodsMap : make(map[string]struct{}), statusHandlerMap : make(map[string]HandlerFunc), serveTree : make(map[string]interface{}), hooksTree : make(map[string]interface{}), @@ -211,9 +219,6 @@ func GetServer(name...interface{}) (*Server) { } // 日志的标准输出默认关闭,但是错误信息会特殊处理 s.logger.SetStdPrint(false) - for _, v := range strings.Split(gHTTP_METHODS, ",") { - s.methodsMap[v] = struct{}{} - } // 初始化时使用默认配置 s.SetConfig(defaultServerConfig) // 记录到全局ServerMap中 diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go index 3c79a04b1..9f791dc0c 100644 --- a/g/net/ghttp/ghttp_server_service_controller.go +++ b/g/net/ghttp/ghttp_server_service_controller.go @@ -10,6 +10,7 @@ package ghttp import ( "errors" "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/text/gregex" "strings" "reflect" "fmt" @@ -63,10 +64,11 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e fname : mname, faddr : nil, } - // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI - // 例如: pattern为/user, 那么会同时注册/user及/user/index, - // 这里处理新增/user路由绑定 - if strings.EqualFold(mname, "Index") { + // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI, + // 例如: pattern为/user, 那么会同时注册/user及/user/index, + // 这里处理新增/user路由绑定。 + // 注意,当pattern带有内置变量时,不会自动加该路由。 + if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { p := gstr.PosR(key, "/index") k := key[0 : p] + key[p + 6 : ] if len(k) == 0 { @@ -126,12 +128,13 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error { m := make(handlerMap) v := reflect.ValueOf(c) t := v.Type() + sname := t.Elem().Name() pkgPath := t.Elem().PkgPath() // 如果存在与HttpMethod对应名字的方法,那么绑定这些方法 for i := 0; i < v.NumMethod(); i++ { mname := t.Method(i).Name method := strings.ToUpper(mname) - if _, ok := s.methodsMap[method]; !ok { + if _, ok := methodsMap[method]; !ok { continue } if _, ok := v.Method(i).Interface().(func()); !ok { @@ -144,7 +147,7 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error { if ctlName[0] == '*' { ctlName = fmt.Sprintf(`(%s)`, ctlName) } - key := mname + ":" + pattern + key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false) m[key] = &handlerItem { name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), rtype : gROUTE_REGISTER_CONTROLLER, diff --git a/g/net/ghttp/ghttp_server_service_handler.go b/g/net/ghttp/ghttp_server_service_handler.go index 2e9e8c542..2ffc6856f 100644 --- a/g/net/ghttp/ghttp_server_service_handler.go +++ b/g/net/ghttp/ghttp_server_service_handler.go @@ -49,7 +49,7 @@ func (s *Server) bindHandlerByMap(m handlerMap) error { // 将内置的名称按照设定的规则合并到pattern中,内置名称按照{.xxx}规则命名。 // 规则1:pattern中的URI包含{.struct}关键字,则替换该关键字为结构体名称; -// 规则1:pattern中的URI包含{.method}关键字,则替换该关键字为方法名称; +// 规则2:pattern中的URI包含{.method}关键字,则替换该关键字为方法名称; // 规则2:如果不满足规则1,那么直接将防发明附加到pattern中的URI后面; func (s *Server) mergeBuildInNameToPattern(pattern string, structName, methodName string, allowAppend bool) string { structName = s.nameToUrlPart(structName) diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go index 22a2bc310..8b3816353 100644 --- a/g/net/ghttp/ghttp_server_service_object.go +++ b/g/net/ghttp/ghttp_server_service_object.go @@ -10,6 +10,7 @@ package ghttp import ( "errors" "github.com/gogf/gf/g/os/glog" + "github.com/gogf/gf/g/text/gregex" "strings" "reflect" "fmt" @@ -72,8 +73,9 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er finit : finit, fshut : fshut, } - // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI - if strings.EqualFold(mname, "Index") { + // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI。 + // 注意,当pattern带有内置变量时,不会自动加该路由。 + if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { p := gstr.PosR(key, "/index") k := key[0 : p] + key[p + 6 : ] if len(k) == 0 { @@ -145,6 +147,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { m := make(handlerMap) v := reflect.ValueOf(obj) t := v.Type() + sname := t.Elem().Name() finit := (func(*Request))(nil) fshut := (func(*Request))(nil) if v.MethodByName("Init").IsValid() { @@ -157,7 +160,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { for i := 0; i < v.NumMethod(); i++ { mname := t.Method(i).Name method := strings.ToUpper(mname) - if _, ok := s.methodsMap[method]; !ok { + if _, ok := methodsMap[method]; !ok { continue } faddr, ok := v.Method(i).Interface().(func(*Request)) @@ -171,7 +174,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error { if objName[0] == '*' { objName = fmt.Sprintf(`(%s)`, objName) } - key := mname + ":" + pattern + key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false) m[key] = &handlerItem { name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname), rtype : gROUTE_REGISTER_OBJECT, diff --git a/g/net/ghttp/ghttp_unit_router_controller_rest_test.go b/g/net/ghttp/ghttp_unit_router_controller_rest_test.go new file mode 100644 index 000000000..cda6e8182 --- /dev/null +++ b/g/net/ghttp/ghttp_unit_router_controller_rest_test.go @@ -0,0 +1,106 @@ +// 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" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/frame/gmvc" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + + +type ControllerRest struct { + gmvc.Controller +} + +func (c *ControllerRest) Init(r *ghttp.Request) { + c.Controller.Init(r) + c.Response.Write("1") +} + +func (c *ControllerRest) Shut() { + c.Response.Write("2") +} + +func (c *ControllerRest) Get() { + c.Response.Write("Controller Get") +} + +func (c *ControllerRest) Put() { + c.Response.Write("Controller Put") +} + +func (c *ControllerRest) Post() { + c.Response.Write("Controller Post") +} + +func (c *ControllerRest) Delete() { + c.Response.Write("Controller Delete") +} + +func (c *ControllerRest) Patch() { + c.Response.Write("Controller Patch") +} + +func (c *ControllerRest) Options() { + c.Response.Write("Controller Options") +} + +func (c *ControllerRest) Head() { + c.Response.Header().Set("head-ok", "1") +} + +// 控制器注册测试 +func Test_Router_ControllerRest(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.BindControllerRest("/", new(ControllerRest)) + s.BindControllerRest("/{.struct}/{.method}", new(ControllerRest)) + 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("/"), "1Controller Get2") + gtest.Assert(client.PutContent("/"), "1Controller Put2") + gtest.Assert(client.PostContent("/"), "1Controller Post2") + gtest.Assert(client.DeleteContent("/"), "1Controller Delete2") + gtest.Assert(client.PatchContent("/"), "1Controller Patch2") + gtest.Assert(client.OptionsContent("/"), "1Controller Options2") + resp1, err := client.Head("/") + if err == nil { + defer resp1.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp1.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/controller-rest/get"), "1Controller Get2") + gtest.Assert(client.PutContent("/controller-rest/put"), "1Controller Put2") + gtest.Assert(client.PostContent("/controller-rest/post"), "1Controller Post2") + gtest.Assert(client.DeleteContent("/controller-rest/delete"), "1Controller Delete2") + gtest.Assert(client.PatchContent("/controller-rest/patch"), "1Controller Patch2") + gtest.Assert(client.OptionsContent("/controller-rest/options"), "1Controller Options2") + resp2, err := client.Head("/controller-rest/head") + if err == nil { + defer resp2.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp2.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/none-exist"), "Not Found") + }) +} diff --git a/g/net/ghttp/ghttp_unit_router_controller_test.go b/g/net/ghttp/ghttp_unit_router_controller_test.go index 3f862a85a..b84280cc7 100644 --- a/g/net/ghttp/ghttp_unit_router_controller_test.go +++ b/g/net/ghttp/ghttp_unit_router_controller_test.go @@ -17,24 +17,24 @@ import ( ) // 控制器 -type ControllerBasic struct { +type Controller struct { gmvc.Controller } -func (c *ControllerBasic) Init(r *ghttp.Request) { +func (c *Controller) Init(r *ghttp.Request) { c.Controller.Init(r) c.Response.Write("1") } -func (c *ControllerBasic) Shut() { +func (c *Controller) Shut() { c.Response.Write("2") } -func (c *ControllerBasic) Index() { +func (c *Controller) Index() { c.Response.Write("Controller Index") } -func (c *ControllerBasic) Show() { +func (c *Controller) Show() { c.Response.Write("Controller Show") } @@ -43,7 +43,8 @@ func (c *ControllerBasic) Show() { func Test_Router_Controller(t *testing.T) { p := ports.PopRand() s := g.Server(p) - s.BindController("/", new(ControllerBasic)) + s.BindController("/", new(Controller)) + s.BindController("/{.struct}/{.method}", new(Controller)) s.SetPort(p) s.SetDumpRouteMap(false) s.Start() @@ -60,6 +61,13 @@ func Test_Router_Controller(t *testing.T) { gtest.Assert(client.GetContent("/shut"), "Not Found") gtest.Assert(client.GetContent("/index"), "1Controller Index2") gtest.Assert(client.GetContent("/show"), "1Controller Show2") + + gtest.Assert(client.GetContent("/controller"), "Not Found") + gtest.Assert(client.GetContent("/controller/init"), "Not Found") + gtest.Assert(client.GetContent("/controller/shut"), "Not Found") + gtest.Assert(client.GetContent("/controller/index"), "1Controller Index2") + gtest.Assert(client.GetContent("/controller/show"), "1Controller Show2") + gtest.Assert(client.GetContent("/none-exist"), "Not Found") }) } diff --git a/g/net/ghttp/ghttp_unit_router_group_rest_test.go b/g/net/ghttp/ghttp_unit_router_group_rest_test.go new file mode 100644 index 000000000..30c243c7f --- /dev/null +++ b/g/net/ghttp/ghttp_unit_router_group_rest_test.go @@ -0,0 +1,173 @@ +// 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" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/frame/gmvc" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +type GroupCtlRest struct { + gmvc.Controller +} + +func (c *GroupCtlRest) Init(r *ghttp.Request) { + c.Controller.Init(r) + c.Response.Write("1") +} + +func (c *GroupCtlRest) Shut() { + c.Response.Write("2") +} + +func (c *GroupCtlRest) Get() { + c.Response.Write("Controller Get") +} + +func (c *GroupCtlRest) Put() { + c.Response.Write("Controller Put") +} + +func (c *GroupCtlRest) Post() { + c.Response.Write("Controller Post") +} + +func (c *GroupCtlRest) Delete() { + c.Response.Write("Controller Delete") +} + +func (c *GroupCtlRest) Patch() { + c.Response.Write("Controller Patch") +} + +func (c *GroupCtlRest) Options() { + c.Response.Write("Controller Options") +} + +func (c *GroupCtlRest) Head() { + c.Response.Header().Set("head-ok", "1") +} + +type GroupObjRest struct {} + +func (o *GroupObjRest) Init(r *ghttp.Request) { + r.Response.Write("1") +} + +func (o *GroupObjRest) Shut(r *ghttp.Request) { + r.Response.Write("2") +} + +func (o *GroupObjRest) Get(r *ghttp.Request) { + r.Response.Write("Object Get") +} + +func (o *GroupObjRest) Put(r *ghttp.Request) { + r.Response.Write("Object Put") +} + +func (o *GroupObjRest) Post(r *ghttp.Request) { + r.Response.Write("Object Post") +} + +func (o *GroupObjRest) Delete(r *ghttp.Request) { + r.Response.Write("Object Delete") +} + +func (o *GroupObjRest) Patch(r *ghttp.Request) { + r.Response.Write("Object Patch") +} + +func (o *GroupObjRest) Options(r *ghttp.Request) { + r.Response.Write("Object Options") +} + +func (o *GroupObjRest) Head(r *ghttp.Request) { + r.Response.Header().Set("head-ok", "1") +} + +func Test_Router_GroupRest(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + g := s.Group("/api") + ctl := new(GroupCtlRest) + obj := new(GroupObjRest) + g.REST("/ctl", ctl) + g.REST("/obj", obj) + g.REST("/{.struct}/{.method}", ctl) + g.REST("/{.struct}/{.method}", obj) + 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("/api/ctl"), "1Controller Get2") + gtest.Assert(client.PutContent("/api/ctl"), "1Controller Put2") + gtest.Assert(client.PostContent("/api/ctl"), "1Controller Post2") + gtest.Assert(client.DeleteContent("/api/ctl"), "1Controller Delete2") + gtest.Assert(client.PatchContent("/api/ctl"), "1Controller Patch2") + gtest.Assert(client.OptionsContent("/api/ctl"), "1Controller Options2") + resp1, err := client.Head("/api/ctl") + if err == nil { + defer resp1.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp1.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/api/obj"), "1Object Get2") + gtest.Assert(client.PutContent("/api/obj"), "1Object Put2") + gtest.Assert(client.PostContent("/api/obj"), "1Object Post2") + gtest.Assert(client.DeleteContent("/api/obj"), "1Object Delete2") + gtest.Assert(client.PatchContent("/api/obj"), "1Object Patch2") + gtest.Assert(client.OptionsContent("/api/obj"), "1Object Options2") + resp2, err := client.Head("/api/obj") + if err == nil { + defer resp2.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp2.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/api/group-ctl-rest"), "Not Found") + gtest.Assert(client.GetContent("/api/group-ctl-rest/get"), "1Controller Get2") + gtest.Assert(client.PutContent("/api/group-ctl-rest/put"), "1Controller Put2") + gtest.Assert(client.PostContent("/api/group-ctl-rest/post"), "1Controller Post2") + gtest.Assert(client.DeleteContent("/api/group-ctl-rest/delete"), "1Controller Delete2") + gtest.Assert(client.PatchContent("/api/group-ctl-rest/patch"), "1Controller Patch2") + gtest.Assert(client.OptionsContent("/api/group-ctl-rest/options"), "1Controller Options2") + resp3, err := client.Head("/api/group-ctl-rest/head") + if err == nil { + defer resp3.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp3.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/api/group-obj-rest"), "Not Found") + gtest.Assert(client.GetContent("/api/group-obj-rest/get"), "1Object Get2") + gtest.Assert(client.PutContent("/api/group-obj-rest/put"), "1Object Put2") + gtest.Assert(client.PostContent("/api/group-obj-rest/post"), "1Object Post2") + gtest.Assert(client.DeleteContent("/api/group-obj-rest/delete"), "1Object Delete2") + gtest.Assert(client.PatchContent("/api/group-obj-rest/patch"), "1Object Patch2") + gtest.Assert(client.OptionsContent("/api/group-obj-rest/options"), "1Object Options2") + resp4, err := client.Head("/api/group-obj-rest/head") + if err == nil { + defer resp4.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp4.Header.Get("head-ok"), "1") + }) +} diff --git a/g/net/ghttp/ghttp_unit_router_group_test.go b/g/net/ghttp/ghttp_unit_router_group_test.go index b355a78c8..67aaa9a94 100644 --- a/g/net/ghttp/ghttp_unit_router_group_test.go +++ b/g/net/ghttp/ghttp_unit_router_group_test.go @@ -18,63 +18,63 @@ import ( ) // 执行对象 -type Object struct {} +type GroupObject struct {} -func (o *Object) Init(r *ghttp.Request) { +func (o *GroupObject) Init(r *ghttp.Request) { r.Response.Write("1") } -func (o *Object) Shut(r *ghttp.Request) { +func (o *GroupObject) Shut(r *ghttp.Request) { r.Response.Write("2") } -func (o *Object) Index(r *ghttp.Request) { +func (o *GroupObject) Index(r *ghttp.Request) { r.Response.Write("Object Index") } -func (o *Object) Show(r *ghttp.Request) { +func (o *GroupObject) Show(r *ghttp.Request) { r.Response.Write("Object Show") } -func (o *Object) Delete(r *ghttp.Request) { - r.Response.Write("Object REST Delete") +func (o *GroupObject) Delete(r *ghttp.Request) { + r.Response.Write("Object Delete") } // 控制器 -type Controller struct { +type GroupController struct { gmvc.Controller } -func (c *Controller) Init(r *ghttp.Request) { +func (c *GroupController) Init(r *ghttp.Request) { c.Controller.Init(r) c.Response.Write("1") } -func (c *Controller) Shut() { +func (c *GroupController) Shut() { c.Response.Write("2") } -func (c *Controller) Index() { +func (c *GroupController) Index() { c.Response.Write("Controller Index") } -func (c *Controller) Show() { +func (c *GroupController) Show() { c.Response.Write("Controller Show") } -func (c *Controller) Post() { - c.Response.Write("Controller REST Post") +func (c *GroupController) Post() { + c.Response.Write("Controller Post") } func Handler(r *ghttp.Request) { r.Response.Write("Handler") } -func Test_Router_Group1(t *testing.T) { +func Test_Router_GroupBasic1(t *testing.T) { p := ports.PopRand() s := g.Server(p) - obj := new(Object) - ctl := new(Controller) + obj := new(GroupObject) + ctl := new(GroupController) // 分组路由方法注册 g := s.Group("/api") g.ALL ("/handler", Handler) @@ -100,31 +100,28 @@ func Test_Router_Group1(t *testing.T) { gtest.Assert(client.GetContent ("/api/ctl/"), "1Controller Index2") gtest.Assert(client.GetContent ("/api/ctl/index"), "1Controller Index2") gtest.Assert(client.GetContent ("/api/ctl/my-show"), "1Controller Show2") - gtest.Assert(client.GetContent ("/api/ctl/post"), "1Controller REST Post2") + gtest.Assert(client.GetContent ("/api/ctl/post"), "1Controller Post2") gtest.Assert(client.GetContent ("/api/ctl/show"), "1Controller Show2") - gtest.Assert(client.PostContent("/api/ctl/rest"), "1Controller REST Post2") + gtest.Assert(client.PostContent("/api/ctl/rest"), "1Controller Post2") gtest.Assert(client.GetContent ("/api/obj"), "1Object Index2") gtest.Assert(client.GetContent ("/api/obj/"), "1Object Index2") gtest.Assert(client.GetContent ("/api/obj/index"), "1Object Index2") - gtest.Assert(client.GetContent ("/api/obj/delete"), "1Object REST Delete2") + gtest.Assert(client.GetContent ("/api/obj/delete"), "1Object Delete2") gtest.Assert(client.GetContent ("/api/obj/my-show"), "1Object Show2") gtest.Assert(client.GetContent ("/api/obj/show"), "1Object Show2") - gtest.Assert(client.DeleteContent("/api/obj/rest"), "1Object REST Delete2") + gtest.Assert(client.DeleteContent("/api/obj/rest"), "1Object Delete2") - // 测试404 - resp, err := client.Get("/ThisDoesNotExist") - defer resp.Close() - gtest.Assert(err, nil) - gtest.Assert(resp.StatusCode, 404) + gtest.Assert(client.DeleteContent("/ThisDoesNotExist"), "Not Found") + gtest.Assert(client.DeleteContent("/api/ThisDoesNotExist"), "Not Found") }) } -func Test_Router_Group2(t *testing.T) { +func Test_Router_Basic2(t *testing.T) { p := ports.PopRand() s := g.Server(p) - obj := new(Object) - ctl := new(Controller) + obj := new(GroupObject) + ctl := new(GroupController) // 分组路由批量注册 s.Group("/api").Bind("/api", []ghttp.GroupItem{ {"ALL", "/handler", Handler}, @@ -148,19 +145,48 @@ func Test_Router_Group2(t *testing.T) { gtest.Assert(client.GetContent ("/api/handler"), "Handler") gtest.Assert(client.GetContent ("/api/ctl/my-show"), "1Controller Show2") - gtest.Assert(client.GetContent ("/api/ctl/post"), "1Controller REST Post2") + gtest.Assert(client.GetContent ("/api/ctl/post"), "1Controller Post2") gtest.Assert(client.GetContent ("/api/ctl/show"), "1Controller Show2") - gtest.Assert(client.PostContent("/api/ctl/rest"), "1Controller REST Post2") + gtest.Assert(client.PostContent("/api/ctl/rest"), "1Controller Post2") - gtest.Assert(client.GetContent ("/api/obj/delete"), "1Object REST Delete2") + gtest.Assert(client.GetContent ("/api/obj/delete"), "1Object Delete2") gtest.Assert(client.GetContent ("/api/obj/my-show"), "1Object Show2") gtest.Assert(client.GetContent ("/api/obj/show"), "1Object Show2") - gtest.Assert(client.DeleteContent("/api/obj/rest"), "1Object REST Delete2") + gtest.Assert(client.DeleteContent("/api/obj/rest"), "1Object Delete2") - // 测试404 - resp, err := client.Get("/ThisDoesNotExist") - defer resp.Close() - gtest.Assert(err, nil) - gtest.Assert(resp.StatusCode, 404) + gtest.Assert(client.DeleteContent("/ThisDoesNotExist"), "Not Found") + gtest.Assert(client.DeleteContent("/api/ThisDoesNotExist"), "Not Found") }) } + +func Test_Router_GroupBuildInVar(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + obj := new(GroupObject) + ctl := new(GroupController) + // 分组路由方法注册 + g := s.Group("/api") + g.ALL ("/{.struct}/{.method}", ctl) + g.ALL ("/{.struct}/{.method}", obj) + 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 ("/api/group-controller/index"), "1Controller Index2") + gtest.Assert(client.GetContent ("/api/group-controller/post"), "1Controller Post2") + gtest.Assert(client.GetContent ("/api/group-controller/show"), "1Controller Show2") + + gtest.Assert(client.GetContent ("/api/group-object/index"), "1Object Index2") + gtest.Assert(client.GetContent ("/api/group-object/delete"), "1Object Delete2") + gtest.Assert(client.GetContent ("/api/group-object/show"), "1Object Show2") + + gtest.Assert(client.DeleteContent("/ThisDoesNotExist"), "Not Found") + gtest.Assert(client.DeleteContent("/api/ThisDoesNotExist"), "Not Found") + }) +} \ No newline at end of file diff --git a/g/net/ghttp/ghttp_unit_router_object_rest_test.go b/g/net/ghttp/ghttp_unit_router_object_rest_test.go new file mode 100644 index 000000000..f0b686813 --- /dev/null +++ b/g/net/ghttp/ghttp_unit_router_object_rest_test.go @@ -0,0 +1,100 @@ +// 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" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +type ObjectRest struct {} + +func (o *ObjectRest) Init(r *ghttp.Request) { + r.Response.Write("1") +} + +func (o *ObjectRest) Shut(r *ghttp.Request) { + r.Response.Write("2") +} + +func (o *ObjectRest) Get(r *ghttp.Request) { + r.Response.Write("Object Get") +} + +func (o *ObjectRest) Put(r *ghttp.Request) { + r.Response.Write("Object Put") +} + +func (o *ObjectRest) Post(r *ghttp.Request) { + r.Response.Write("Object Post") +} + +func (o *ObjectRest) Delete(r *ghttp.Request) { + r.Response.Write("Object Delete") +} + +func (o *ObjectRest) Patch(r *ghttp.Request) { + r.Response.Write("Object Patch") +} + +func (o *ObjectRest) Options(r *ghttp.Request) { + r.Response.Write("Object Options") +} + +func (o *ObjectRest) Head(r *ghttp.Request) { + r.Response.Header().Set("head-ok", "1") +} + +func Test_Router_ObjectRest(t *testing.T) { + p := ports.PopRand() + s := g.Server(p) + s.BindObjectRest("/", new(ObjectRest)) + s.BindObjectRest("/{.struct}/{.method}", new(ObjectRest)) + 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("/"), "1Object Get2") + gtest.Assert(client.PutContent("/"), "1Object Put2") + gtest.Assert(client.PostContent("/"), "1Object Post2") + gtest.Assert(client.DeleteContent("/"), "1Object Delete2") + gtest.Assert(client.PatchContent("/"), "1Object Patch2") + gtest.Assert(client.OptionsContent("/"), "1Object Options2") + resp1, err := client.Head("/") + if err == nil { + defer resp1.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp1.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/object-rest/get"), "1Object Get2") + gtest.Assert(client.PutContent("/object-rest/put"), "1Object Put2") + gtest.Assert(client.PostContent("/object-rest/post"), "1Object Post2") + gtest.Assert(client.DeleteContent("/object-rest/delete"), "1Object Delete2") + gtest.Assert(client.PatchContent("/object-rest/patch"), "1Object Patch2") + gtest.Assert(client.OptionsContent("/object-rest/options"), "1Object Options2") + resp2, err := client.Head("/object-rest/head") + if err == nil { + defer resp2.Close() + } + gtest.Assert(err, nil) + gtest.Assert(resp2.Header.Get("head-ok"), "1") + + gtest.Assert(client.GetContent("/none-exist"), "Not Found") + }) +} diff --git a/g/net/ghttp/ghttp_unit_router_object_test.go b/g/net/ghttp/ghttp_unit_router_object_test.go index 7852f0fa7..0dc73196a 100644 --- a/g/net/ghttp/ghttp_unit_router_object_test.go +++ b/g/net/ghttp/ghttp_unit_router_object_test.go @@ -15,22 +15,21 @@ import ( "time" ) -// 执行对象 -type ObjectBasic struct {} +type Object struct {} -func (o *ObjectBasic) Init(r *ghttp.Request) { +func (o *Object) Init(r *ghttp.Request) { r.Response.Write("1") } -func (o *ObjectBasic) Shut(r *ghttp.Request) { +func (o *Object) Shut(r *ghttp.Request) { r.Response.Write("2") } -func (o *ObjectBasic) Index(r *ghttp.Request) { +func (o *Object) Index(r *ghttp.Request) { r.Response.Write("Object Index") } -func (o *ObjectBasic) Show(r *ghttp.Request) { +func (o *Object) Show(r *ghttp.Request) { r.Response.Write("Object Show") } @@ -38,7 +37,8 @@ func (o *ObjectBasic) Show(r *ghttp.Request) { func Test_Router_Object(t *testing.T) { p := ports.PopRand() s := g.Server(p) - s.BindObject("/", new(ObjectBasic)) + s.BindObject("/", new(Object)) + s.BindObject("/{.struct}/{.method}", new(Object)) s.SetPort(p) s.SetDumpRouteMap(false) s.Start() @@ -55,6 +55,13 @@ func Test_Router_Object(t *testing.T) { gtest.Assert(client.GetContent("/shut"), "Not Found") gtest.Assert(client.GetContent("/index"), "1Object Index2") gtest.Assert(client.GetContent("/show"), "1Object Show2") + + gtest.Assert(client.GetContent("/object"), "Not Found") + gtest.Assert(client.GetContent("/object/init"), "Not Found") + gtest.Assert(client.GetContent("/object/shut"), "Not Found") + gtest.Assert(client.GetContent("/object/index"), "1Object Index2") + gtest.Assert(client.GetContent("/object/show"), "1Object Show2") + gtest.Assert(client.GetContent("/none-exist"), "Not Found") }) } diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index 906a8eff8..025b8d0b7 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -70,12 +70,16 @@ func (c *Config) filePath(file...string) (path string) { }) if path == "" { buffer := bytes.NewBuffer(nil) - buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) - c.paths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v)) - } - }) + if c.paths.Len() > 0 { + buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name)) + c.paths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name)) + } glog.Error(buffer.String()) } return path @@ -96,7 +100,7 @@ func (c *Config) SetPath(path string) error { c.jsons.Clear() c.paths.Clear() c.paths.Append(realPath) - glog.Debug("[gcfg] SetPath:", realPath) + //glog.Debug("[gcfg] SetPath:", realPath) return nil } @@ -120,7 +124,7 @@ func (c *Config) AddPath(path string) error { return nil } c.paths.Append(realPath) - glog.Debug("[gcfg] AddPath:", realPath) + //glog.Debug("[gcfg] AddPath:", realPath) return nil } @@ -142,7 +146,7 @@ func (c *Config) GetFilePath(file...string) (path string) { // 设置配置管理对象的默认文件名称 func (c *Config) SetFileName(name string) { - glog.Debug("[gcfg] SetFileName:", name) + //glog.Debug("[gcfg] SetFileName:", name) c.name.Set(name) } diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go index ee51bda83..9fa6a3271 100644 --- a/g/os/gview/gview.go +++ b/g/os/gview/gview.go @@ -121,7 +121,7 @@ func (view *View) SetPath(path string) error { } view.paths.Clear() view.paths.Append(realPath) - glog.Debug("[gview] SetPath:", realPath) + //glog.Debug("[gview] SetPath:", realPath) return nil } @@ -141,7 +141,7 @@ func (view *View) AddPath(path string) error { return nil } view.paths.Append(realPath) - glog.Debug("[gview] AddPath:", realPath) + //glog.Debug("[gview] AddPath:", realPath) return nil } @@ -173,12 +173,16 @@ func (view *View) Parse(file string, params Params, funcmap...map[string]interfa }) if path == "" { buffer := bytes.NewBuffer(nil) - buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" in following paths:", file)) - view.paths.RLockFunc(func(array []string) { - for k, v := range array { - buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v)) - } - }) + if view.paths.Len() > 0 { + buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" in following paths:", file)) + view.paths.RLockFunc(func(array []string) { + for k, v := range array { + buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v)) + } + }) + } else { + buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" with no path set/add", file)) + } glog.Error(buffer.String()) return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file)) } diff --git a/geg/net/ghttp/server/hooks/hooks3.go b/geg/net/ghttp/server/hooks/cors1.go similarity index 100% rename from geg/net/ghttp/server/hooks/hooks3.go rename to geg/net/ghttp/server/hooks/cors1.go diff --git a/geg/net/ghttp/server/hooks/cors2.go b/geg/net/ghttp/server/hooks/cors2.go new file mode 100644 index 000000000..31c467a84 --- /dev/null +++ b/geg/net/ghttp/server/hooks/cors2.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/frame/gmvc" + "github.com/gogf/gf/g/net/ghttp" +) + +type Order2 struct { + gmvc.Controller +} + +func (o *Order2) Get() { + o.Response.Write("GET") +} + +func main() { + s := g.Server() + s.BindHookHandlerByMap("/api.v2/*any", map[string]ghttp.HandlerFunc { + "BeforeServe" : func(r *ghttp.Request) { + r.Response.CORSDefault() + }, + }) + s.BindControllerRest("/api.v2/{.struct}", new(Order2)) + s.SetPort(8199) + s.Run() +} \ No newline at end of file