diff --git a/contrib/sdk/httpclient/httpclient.go b/contrib/sdk/httpclient/httpclient.go index 4829c410b..3aa02add2 100644 --- a/contrib/sdk/httpclient/httpclient.go +++ b/contrib/sdk/httpclient/httpclient.go @@ -24,13 +24,13 @@ import ( "github.com/gogf/gf/v2/util/gtag" ) -// Client is a http client for SDK. +// Client is an http client for SDK. type Client struct { *gclient.Client config Config } -// New creates and returns a http client for SDK. +// New creates and returns an http client for SDK. func New(config Config) *Client { client := config.Client if client == nil { diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 2726cf862..96059856c 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -20,6 +20,7 @@ import ( "github.com/olekukonko/tablewriter" "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/container/gtype" "github.com/gogf/gf/v2/debug/gdebug" "github.com/gogf/gf/v2/errors/gcode" @@ -340,8 +341,9 @@ func (s *Server) GetOpenApi() *goai.OpenApiV3 { // GetRoutes retrieves and returns the router array. func (s *Server) GetRoutes() []RouterItem { var ( - m = make(map[string]*garray.SortedArray) - address = s.GetListenedAddress() + m = make(map[string]*garray.SortedArray) + routeFilterSet = gset.NewStrSet() + address = s.GetListenedAddress() ) if s.config.HTTPSAddr != "" { if len(address) > 0 { @@ -351,18 +353,21 @@ func (s *Server) GetRoutes() []RouterItem { } for k, handlerItems := range s.routesMap { array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k) - for index, handlerItem := range handlerItems { - item := RouterItem{ - Server: s.config.Name, - Address: address, - Domain: array[4], - Type: handlerItem.Type, - Middleware: array[1], - Method: array[2], - Route: array[3], - Priority: len(handlerItems) - index - 1, - Handler: handlerItem, - } + for index := len(handlerItems) - 1; index >= 0; index-- { + var ( + handlerItem = handlerItems[index] + item = RouterItem{ + Server: s.config.Name, + Address: address, + Domain: array[4], + Type: handlerItem.Type, + Middleware: array[1], + Method: array[2], + Route: array[3], + Priority: index, + Handler: handlerItem, + } + ) switch item.Handler.Type { case HandlerTypeObject, HandlerTypeHandler: item.IsServiceHandler = true @@ -370,6 +375,14 @@ func (s *Server) GetRoutes() []RouterItem { case HandlerTypeMiddleware: item.Middleware = "GLOBAL MIDDLEWARE" } + // Repeated route filtering for dump. + var setKey = fmt.Sprintf( + `%s|%s|%s|%s`, + item.Method, item.Route, item.Domain, item.Type, + ) + if !routeFilterSet.AddIfNotExist(setKey) { + continue + } if len(item.Handler.Middleware) > 0 { for _, v := range item.Handler.Middleware { if item.Middleware != "" { diff --git a/net/ghttp/ghttp_server_router_group.go b/net/ghttp/ghttp_server_router_group.go index 061c4c754..449367b08 100644 --- a/net/ghttp/ghttp_server_router_group.go +++ b/net/ghttp/ghttp_server_router_group.go @@ -169,8 +169,12 @@ func (g *RouterGroup) Bind(handlerOrObject ...interface{}) *RouterGroup { "/", item, ) + default: - g.server.Logger().Fatalf(ctx, "invalid bind parameter type: %v", originValueAndKind.InputValue.Type()) + g.server.Logger().Fatalf( + ctx, "invalid bind parameter type: %v, should be route function or struct object", + originValueAndKind.InputValue.Type(), + ) } } return group diff --git a/net/ghttp/ghttp_server_router_serve.go b/net/ghttp/ghttp_server_router_serve.go index a4f5b814a..a4297414d 100644 --- a/net/ghttp/ghttp_server_router_serve.go +++ b/net/ghttp/ghttp_server_router_serve.go @@ -187,6 +187,8 @@ func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*Han repeatHandlerCheckMap[item.Id] = struct{}{} } // Serving handler can only be added to the handler array just once. + // The first route item in the list has the most priority than the rest. + // This ignoring can implement route overwritten feature. if hasServe { switch item.Type { case HandlerTypeHandler, HandlerTypeObject: diff --git a/net/ghttp/ghttp_z_unit_feature_router_group_test.go b/net/ghttp/ghttp_z_unit_feature_router_group_test.go index 3eedb8133..b5ac0eff0 100644 --- a/net/ghttp/ghttp_z_unit_feature_router_group_test.go +++ b/net/ghttp/ghttp_z_unit_feature_router_group_test.go @@ -7,13 +7,17 @@ package ghttp_test import ( + "bytes" "fmt" + "sync" "testing" "time" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/glog" "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/guid" ) @@ -177,3 +181,55 @@ func Test_Router_Group_Map(t *testing.T) { t.Assert(c.PostContent(ctx, "/test"), "post") }) } + +type SafeBuffer struct { + buffer *bytes.Buffer + mu sync.Mutex +} + +func (b *SafeBuffer) Write(p []byte) (n int, err error) { + b.mu.Lock() + defer b.mu.Unlock() + return b.buffer.Write(p) +} + +func (b *SafeBuffer) String() string { + b.mu.Lock() + defer b.mu.Unlock() + return b.buffer.String() +} + +func Test_Router_OverWritten(t *testing.T) { + var ( + s = g.Server(guid.S()) + obj = new(GroupObject) + buf = &SafeBuffer{ + buffer: bytes.NewBuffer(nil), + mu: sync.Mutex{}, + } + logger = glog.NewWithWriter(buf) + ) + logger.SetStdoutPrint(false) + s.SetLogger(logger) + s.SetRouteOverWrite(true) + s.Group("/api", func(group *ghttp.RouterGroup) { + group.ALLMap(g.Map{ + "/obj": obj, + }) + group.ALLMap(g.Map{ + "/obj": obj, + }) + }) + s.Start() + defer s.Shutdown() + + dumpContent := buf.String() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + t.Assert(gstr.Count(dumpContent, `/api/obj `), 1) + t.Assert(gstr.Count(dumpContent, `/api/obj/index`), 1) + t.Assert(gstr.Count(dumpContent, `/api/obj/show`), 1) + t.Assert(gstr.Count(dumpContent, `/api/obj/delete`), 1) + }) +}