github.com/wangyougui/gf/v2@v2.6.5/net/ghttp/ghttp_z_unit_feature_router_strict_test.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package ghttp_test 8 9 import ( 10 "context" 11 "fmt" 12 "testing" 13 "time" 14 15 "github.com/wangyougui/gf/v2/encoding/gjson" 16 "github.com/wangyougui/gf/v2/errors/gerror" 17 "github.com/wangyougui/gf/v2/frame/g" 18 "github.com/wangyougui/gf/v2/net/ghttp" 19 "github.com/wangyougui/gf/v2/test/gtest" 20 "github.com/wangyougui/gf/v2/util/guid" 21 ) 22 23 func Test_Router_Handler_Strict_WithObject(t *testing.T) { 24 type TestReq struct { 25 Age int 26 Name string 27 } 28 type TestRes struct { 29 Id int 30 Age int 31 Name string 32 } 33 s := g.Server(guid.S()) 34 s.Use(ghttp.MiddlewareHandlerResponse) 35 s.BindHandler("/test", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { 36 return &TestRes{ 37 Id: 1, 38 Age: req.Age, 39 Name: req.Name, 40 }, nil 41 }) 42 s.BindHandler("/test/error", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { 43 return &TestRes{ 44 Id: 1, 45 Age: req.Age, 46 Name: req.Name, 47 }, gerror.New("error") 48 }) 49 s.SetDumpRouterMap(false) 50 s.Start() 51 defer s.Shutdown() 52 53 time.Sleep(100 * time.Millisecond) 54 gtest.C(t, func(t *gtest.T) { 55 client := g.Client() 56 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 57 58 t.Assert(client.GetContent(ctx, "/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) 59 t.Assert(client.GetContent(ctx, "/test/error"), `{"code":50,"message":"error","data":{"Id":1,"Age":0,"Name":""}}`) 60 }) 61 } 62 63 type TestForHandlerWithObjectAndMeta1Req struct { 64 g.Meta `path:"/custom-test1" method:"get"` 65 Age int 66 Name string 67 } 68 69 type TestForHandlerWithObjectAndMeta1Res struct { 70 Id int 71 Age int 72 } 73 74 type TestForHandlerWithObjectAndMeta2Req struct { 75 g.Meta `path:"/custom-test2" method:"get"` 76 Age int 77 Name string 78 } 79 80 type TestForHandlerWithObjectAndMeta2Res struct { 81 Id int 82 Name string 83 } 84 85 type ControllerForHandlerWithObjectAndMeta1 struct{} 86 87 func (ControllerForHandlerWithObjectAndMeta1) Index(ctx context.Context, req *TestForHandlerWithObjectAndMeta1Req) (res *TestForHandlerWithObjectAndMeta1Res, err error) { 88 return &TestForHandlerWithObjectAndMeta1Res{ 89 Id: 1, 90 Age: req.Age, 91 }, nil 92 } 93 94 func (ControllerForHandlerWithObjectAndMeta1) Test2(ctx context.Context, req *TestForHandlerWithObjectAndMeta2Req) (res *TestForHandlerWithObjectAndMeta2Res, err error) { 95 return &TestForHandlerWithObjectAndMeta2Res{ 96 Id: 1, 97 Name: req.Name, 98 }, nil 99 } 100 101 type TestForHandlerWithObjectAndMeta3Req struct { 102 g.Meta `path:"/custom-test3" method:"get"` 103 Age int 104 Name string 105 } 106 107 type TestForHandlerWithObjectAndMeta3Res struct { 108 Id int 109 Age int 110 } 111 112 type TestForHandlerWithObjectAndMeta4Req struct { 113 g.Meta `path:"/custom-test4" method:"get"` 114 Age int 115 Name string 116 } 117 118 type TestForHandlerWithObjectAndMeta4Res struct { 119 Id int 120 Name string 121 } 122 123 type ControllerForHandlerWithObjectAndMeta2 struct{} 124 125 func (ControllerForHandlerWithObjectAndMeta2) Test3(ctx context.Context, req *TestForHandlerWithObjectAndMeta3Req) (res *TestForHandlerWithObjectAndMeta3Res, err error) { 126 return &TestForHandlerWithObjectAndMeta3Res{ 127 Id: 1, 128 Age: req.Age, 129 }, nil 130 } 131 132 func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *TestForHandlerWithObjectAndMeta4Req) (res *TestForHandlerWithObjectAndMeta4Res, err error) { 133 return &TestForHandlerWithObjectAndMeta4Res{ 134 Id: 1, 135 Name: req.Name, 136 }, nil 137 } 138 139 func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) { 140 s := g.Server(guid.S()) 141 s.Use(ghttp.MiddlewareHandlerResponse) 142 s.Group("/", func(group *ghttp.RouterGroup) { 143 group.ALL("/", new(ControllerForHandlerWithObjectAndMeta1)) 144 }) 145 s.SetDumpRouterMap(false) 146 s.Start() 147 defer s.Shutdown() 148 149 time.Sleep(100 * time.Millisecond) 150 gtest.C(t, func(t *gtest.T) { 151 client := g.Client() 152 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 153 154 t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`) 155 t.Assert(client.GetContent(ctx, "/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 156 t.Assert(client.GetContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 157 t.Assert(client.PostContent(ctx, "/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`) 158 }) 159 } 160 161 func Test_Router_Handler_Strict_Group_Bind(t *testing.T) { 162 s := g.Server(guid.S()) 163 s.Use(ghttp.MiddlewareHandlerResponse) 164 s.Group("/api/v1", func(group *ghttp.RouterGroup) { 165 group.Bind( 166 new(ControllerForHandlerWithObjectAndMeta1), 167 new(ControllerForHandlerWithObjectAndMeta2), 168 ) 169 }) 170 s.Group("/api/v2", func(group *ghttp.RouterGroup) { 171 group.Bind( 172 new(ControllerForHandlerWithObjectAndMeta1), 173 new(ControllerForHandlerWithObjectAndMeta2), 174 ) 175 }) 176 s.SetDumpRouterMap(false) 177 s.Start() 178 defer s.Shutdown() 179 180 time.Sleep(100 * time.Millisecond) 181 gtest.C(t, func(t *gtest.T) { 182 client := g.Client() 183 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 184 185 t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`) 186 t.Assert(client.GetContent(ctx, "/api/v1/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 187 t.Assert(client.GetContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 188 t.Assert(client.PostContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`) 189 190 t.Assert(client.GetContent(ctx, "/api/v1/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 191 t.Assert(client.GetContent(ctx, "/api/v1/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 192 193 t.Assert(client.GetContent(ctx, "/api/v2/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 194 t.Assert(client.GetContent(ctx, "/api/v2/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 195 t.Assert(client.GetContent(ctx, "/api/v2/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 196 t.Assert(client.GetContent(ctx, "/api/v2/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 197 }) 198 } 199 200 func Test_Issue1708(t *testing.T) { 201 type Test struct { 202 Name string `json:"name"` 203 } 204 type Req struct { 205 Page int `json:"page" dc:"分页码"` 206 Size int `json:"size" dc:"分页数量"` 207 TargetType string `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"` 208 TargetId uint `json:"targetId" v:"required#评论目标ID错误" dc:"对应内容ID"` 209 Test [][]Test `json:"test"` 210 } 211 type Res struct { 212 Page int `json:"page" dc:"分页码"` 213 Size int `json:"size" dc:"分页数量"` 214 TargetType string `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"` 215 TargetId uint `json:"targetId" v:"required#评论目标ID错误" dc:"对应内容ID"` 216 Test [][]Test `json:"test"` 217 } 218 219 s := g.Server(guid.S()) 220 s.Use(ghttp.MiddlewareHandlerResponse) 221 s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) { 222 return &Res{ 223 Page: req.Page, 224 Size: req.Size, 225 TargetType: req.TargetType, 226 TargetId: req.TargetId, 227 Test: req.Test, 228 }, nil 229 }) 230 s.SetDumpRouterMap(false) 231 s.Start() 232 defer s.Shutdown() 233 234 time.Sleep(100 * time.Millisecond) 235 gtest.C(t, func(t *gtest.T) { 236 client := g.Client() 237 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 238 content := ` 239 { 240 "targetType":"topic", 241 "targetId":10785, 242 "test":[ 243 [ 244 { 245 "name":"123" 246 } 247 ] 248 ] 249 } 250 ` 251 t.Assert( 252 client.PostContent(ctx, "/test", content), 253 `{"code":0,"message":"","data":{"page":0,"size":0,"targetType":"topic","targetId":10785,"test":[[{"name":"123"}]]}}`, 254 ) 255 }) 256 } 257 258 func Test_Custom_Slice_Type_Attribute(t *testing.T) { 259 type ( 260 WhiteListKey string 261 WhiteListValues []string 262 WhiteList map[WhiteListKey]WhiteListValues 263 ) 264 type Req struct { 265 Id int 266 List WhiteList 267 } 268 type Res struct { 269 Content string 270 } 271 272 s := g.Server(guid.S()) 273 s.Use(ghttp.MiddlewareHandlerResponse) 274 s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) { 275 return &Res{ 276 Content: gjson.MustEncodeString(req), 277 }, nil 278 }) 279 s.SetDumpRouterMap(false) 280 s.Start() 281 defer s.Shutdown() 282 283 time.Sleep(100 * time.Millisecond) 284 gtest.C(t, func(t *gtest.T) { 285 client := g.Client() 286 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 287 content := ` 288 { 289 "id":1, 290 "list":{ 291 "key": ["a", "b", "c"] 292 } 293 } 294 ` 295 t.Assert( 296 client.PostContent(ctx, "/test", content), 297 `{"code":0,"message":"","data":{"Content":"{\"Id\":1,\"List\":{\"key\":[\"a\",\"b\",\"c\"]}}"}}`, 298 ) 299 }) 300 } 301 302 func Test_Router_Handler_Strict_WithGeneric(t *testing.T) { 303 type TestReq struct { 304 Age int 305 } 306 type TestGeneric[T any] struct { 307 Test T 308 } 309 type Test1Res struct { 310 Age TestGeneric[int] 311 } 312 type Test2Res TestGeneric[int] 313 type TestGenericRes[T any] struct { 314 Test T 315 } 316 317 s := g.Server(guid.S()) 318 s.Use(ghttp.MiddlewareHandlerResponse) 319 s.BindHandler("/test1", func(ctx context.Context, req *TestReq) (res *Test1Res, err error) { 320 return &Test1Res{ 321 Age: TestGeneric[int]{ 322 Test: req.Age, 323 }, 324 }, nil 325 }) 326 s.BindHandler("/test1_slice", func(ctx context.Context, req *TestReq) (res []Test1Res, err error) { 327 return []Test1Res{ 328 Test1Res{ 329 Age: TestGeneric[int]{ 330 Test: req.Age, 331 }, 332 }, 333 }, nil 334 }) 335 s.BindHandler("/test2", func(ctx context.Context, req *TestReq) (res *Test2Res, err error) { 336 return &Test2Res{ 337 Test: req.Age, 338 }, nil 339 }) 340 341 s.BindHandler("/test2_slice", func(ctx context.Context, req *TestReq) (res []Test2Res, err error) { 342 return []Test2Res{ 343 Test2Res{ 344 Test: req.Age, 345 }, 346 }, nil 347 }) 348 349 s.BindHandler("/test3", func(ctx context.Context, req *TestReq) (res *TestGenericRes[int], err error) { 350 return &TestGenericRes[int]{ 351 Test: req.Age, 352 }, nil 353 }) 354 355 s.SetDumpRouterMap(false) 356 s.Start() 357 defer s.Shutdown() 358 359 s.BindHandler("/test3_slice", func(ctx context.Context, req *TestReq) (res []TestGenericRes[int], err error) { 360 return []TestGenericRes[int]{ 361 TestGenericRes[int]{ 362 Test: req.Age, 363 }, 364 }, nil 365 }) 366 367 time.Sleep(100 * time.Millisecond) 368 gtest.C(t, func(t *gtest.T) { 369 client := g.Client() 370 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 371 372 t.Assert(client.GetContent(ctx, "/test1?age=1"), `{"code":0,"message":"","data":{"Age":{"Test":1}}}`) 373 t.Assert(client.GetContent(ctx, "/test1_slice?age=1"), `{"code":0,"message":"","data":[{"Age":{"Test":1}}]}`) 374 t.Assert(client.GetContent(ctx, "/test2?age=2"), `{"code":0,"message":"","data":{"Test":2}}`) 375 t.Assert(client.GetContent(ctx, "/test2_slice?age=2"), `{"code":0,"message":"","data":[{"Test":2}]}`) 376 t.Assert(client.GetContent(ctx, "/test3?age=3"), `{"code":0,"message":"","data":{"Test":3}}`) 377 t.Assert(client.GetContent(ctx, "/test3_slice?age=3"), `{"code":0,"message":"","data":[{"Test":3}]}`) 378 }) 379 } 380 381 type ParameterCaseSensitiveController struct{} 382 383 type ParameterCaseSensitiveControllerPathReq struct { 384 g.Meta `path:"/api/*path" method:"post"` 385 Path string 386 } 387 388 type ParameterCaseSensitiveControllerPathRes struct { 389 Path string 390 } 391 392 func (c *ParameterCaseSensitiveController) Path( 393 ctx context.Context, 394 req *ParameterCaseSensitiveControllerPathReq, 395 ) (res *ParameterCaseSensitiveControllerPathRes, err error) { 396 return &ParameterCaseSensitiveControllerPathRes{Path: req.Path}, nil 397 } 398 399 func Test_Router_Handler_Strict_ParameterCaseSensitive(t *testing.T) { 400 s := g.Server(guid.S()) 401 s.Use(ghttp.MiddlewareHandlerResponse) 402 s.Group("/", func(group *ghttp.RouterGroup) { 403 group.Bind(&ParameterCaseSensitiveController{}) 404 }) 405 s.SetDumpRouterMap(false) 406 s.Start() 407 defer s.Shutdown() 408 409 time.Sleep(100 * time.Millisecond) 410 gtest.C(t, func(t *gtest.T) { 411 client := g.Client() 412 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 413 414 for i := 0; i < 1000; i++ { 415 t.Assert( 416 client.PostContent(ctx, "/api/111", `{"Path":"222"}`), 417 `{"code":0,"message":"","data":{"Path":"222"}}`, 418 ) 419 } 420 }) 421 }