From 3df59693482a0c3a7014894fef495308f872c728 Mon Sep 17 00:00:00 2001 From: Wankko Ree Date: Wed, 16 Aug 2023 21:42:36 +0800 Subject: [PATCH] fix generic check with slice for ghttp (#2850) --- net/ghttp/ghttp_server_service_handler.go | 8 +- ...ghttp_z_unit_feature_router_strict_test.go | 77 +++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/net/ghttp/ghttp_server_service_handler.go b/net/ghttp/ghttp_server_service_handler.go index 90a2deebd42..e0e97270390 100644 --- a/net/ghttp/ghttp_server_service_handler.go +++ b/net/ghttp/ghttp_server_service_handler.go @@ -213,10 +213,12 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth func trimGeneric(structName string) string { var ( - leftBraceIndex = strings.Index(structName, "[") - rightBraceIndex = strings.Index(structName, "]") + leftBraceIndex = strings.LastIndex(structName, "[") // for generic, it is faster to start at the end than at the beginning + rightBraceIndex = strings.LastIndex(structName, "]") ) - if leftBraceIndex == -1 || rightBraceIndex == -1 { + if leftBraceIndex == -1 || rightBraceIndex == -1 { // not found '[' or ']' + return structName + } else if leftBraceIndex+1 == rightBraceIndex { // may be a slice, because generic is '[X]', not '[]' return structName } return structName[:leftBraceIndex] diff --git a/net/ghttp/ghttp_z_unit_feature_router_strict_test.go b/net/ghttp/ghttp_z_unit_feature_router_strict_test.go index 74d82fda77c..664e1b49dd4 100644 --- a/net/ghttp/ghttp_z_unit_feature_router_strict_test.go +++ b/net/ghttp/ghttp_z_unit_feature_router_strict_test.go @@ -298,3 +298,80 @@ func Test_Custom_Slice_Type_Attribute(t *testing.T) { ) }) } + +func Test_Router_Handler_Strict_WithGeneric(t *testing.T) { + type TestReq struct { + Age int + } + type TestGeneric[T any] struct { + Test T + } + type Test1Res struct { + Age TestGeneric[int] + } + type Test2Res TestGeneric[int] + type TestGenericRes[T any] struct { + Test T + } + + s := g.Server(guid.S()) + s.Use(ghttp.MiddlewareHandlerResponse) + s.BindHandler("/test1", func(ctx context.Context, req *TestReq) (res *Test1Res, err error) { + return &Test1Res{ + Age: TestGeneric[int]{ + Test: req.Age, + }, + }, nil + }) + s.BindHandler("/test1_slice", func(ctx context.Context, req *TestReq) (res []Test1Res, err error) { + return []Test1Res{ + Test1Res{ + Age: TestGeneric[int]{ + Test: req.Age, + }, + }, + }, nil + }) + s.BindHandler("/test2", func(ctx context.Context, req *TestReq) (res *Test2Res, err error) { + return &Test2Res{ + Test: req.Age, + }, nil + }) + s.BindHandler("/test2_slice", func(ctx context.Context, req *TestReq) (res []Test2Res, err error) { + return []Test2Res{ + Test2Res{ + Test: req.Age, + }, + }, nil + }) + + s.BindHandler("/test3", func(ctx context.Context, req *TestReq) (res *TestGenericRes[int], err error) { + return &TestGenericRes[int]{ + Test: req.Age, + }, nil + }) + s.BindHandler("/test3_slice", func(ctx context.Context, req *TestReq) (res []TestGenericRes[int], err error) { + return []TestGenericRes[int]{ + TestGenericRes[int]{ + Test: req.Age, + }, + }, nil + }) + + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + client := g.Client() + client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) + + t.Assert(client.GetContent(ctx, "/test1?age=1"), `{"code":0,"message":"","data":{"Age":{"Test":1}}}`) + t.Assert(client.GetContent(ctx, "/test1_slice?age=1"), `{"code":0,"message":"","data":[{"Age":{"Test":1}}]}`) + t.Assert(client.GetContent(ctx, "/test2?age=2"), `{"code":0,"message":"","data":{"Test":2}}`) + t.Assert(client.GetContent(ctx, "/test2_slice?age=2"), `{"code":0,"message":"","data":[{"Test":2}]}`) + t.Assert(client.GetContent(ctx, "/test3?age=3"), `{"code":0,"message":"","data":{"Test":3}}`) + t.Assert(client.GetContent(ctx, "/test3_slice?age=3"), `{"code":0,"message":"","data":[{"Test":3}]}`) + }) +}