github.com/gogf/gf/v2@v2.7.4/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/gogf/gf. 6 7 package ghttp_test 8 9 import ( 10 "context" 11 "fmt" 12 "testing" 13 "time" 14 15 "github.com/gogf/gf/v2/encoding/gjson" 16 "github.com/gogf/gf/v2/errors/gerror" 17 "github.com/gogf/gf/v2/frame/g" 18 "github.com/gogf/gf/v2/internal/json" 19 "github.com/gogf/gf/v2/net/ghttp" 20 "github.com/gogf/gf/v2/test/gtest" 21 "github.com/gogf/gf/v2/util/guid" 22 ) 23 24 func Test_Router_Handler_Strict_WithObject(t *testing.T) { 25 type TestReq struct { 26 Age int 27 Name string 28 } 29 type TestRes struct { 30 Id int 31 Age int 32 Name string 33 } 34 s := g.Server(guid.S()) 35 s.Use(ghttp.MiddlewareHandlerResponse) 36 s.BindHandler("/test", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { 37 return &TestRes{ 38 Id: 1, 39 Age: req.Age, 40 Name: req.Name, 41 }, nil 42 }) 43 s.BindHandler("/test/error", func(ctx context.Context, req *TestReq) (res *TestRes, err error) { 44 return &TestRes{ 45 Id: 1, 46 Age: req.Age, 47 Name: req.Name, 48 }, gerror.New("error") 49 }) 50 s.SetDumpRouterMap(false) 51 s.Start() 52 defer s.Shutdown() 53 54 time.Sleep(100 * time.Millisecond) 55 gtest.C(t, func(t *gtest.T) { 56 client := g.Client() 57 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 58 59 t.Assert(client.GetContent(ctx, "/test?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18,"Name":"john"}}`) 60 t.Assert(client.GetContent(ctx, "/test/error"), `{"code":50,"message":"error","data":{"Id":1,"Age":0,"Name":""}}`) 61 }) 62 } 63 64 type TestForHandlerWithObjectAndMeta1Req struct { 65 g.Meta `path:"/custom-test1" method:"get"` 66 Age int 67 Name string 68 } 69 70 type TestForHandlerWithObjectAndMeta1Res struct { 71 Id int 72 Age int 73 } 74 75 type TestForHandlerWithObjectAndMeta2Req struct { 76 g.Meta `path:"/custom-test2" method:"get"` 77 Age int 78 Name string 79 } 80 81 type TestForHandlerWithObjectAndMeta2Res struct { 82 Id int 83 Name string 84 } 85 86 type ControllerForHandlerWithObjectAndMeta1 struct{} 87 88 func (ControllerForHandlerWithObjectAndMeta1) Index(ctx context.Context, req *TestForHandlerWithObjectAndMeta1Req) (res *TestForHandlerWithObjectAndMeta1Res, err error) { 89 return &TestForHandlerWithObjectAndMeta1Res{ 90 Id: 1, 91 Age: req.Age, 92 }, nil 93 } 94 95 func (ControllerForHandlerWithObjectAndMeta1) Test2(ctx context.Context, req *TestForHandlerWithObjectAndMeta2Req) (res *TestForHandlerWithObjectAndMeta2Res, err error) { 96 return &TestForHandlerWithObjectAndMeta2Res{ 97 Id: 1, 98 Name: req.Name, 99 }, nil 100 } 101 102 type TestForHandlerWithObjectAndMeta3Req struct { 103 g.Meta `path:"/custom-test3" method:"get"` 104 Age int 105 Name string 106 } 107 108 type TestForHandlerWithObjectAndMeta3Res struct { 109 Id int 110 Age int 111 } 112 113 type TestForHandlerWithObjectAndMeta4Req struct { 114 g.Meta `path:"/custom-test4" method:"get"` 115 Age int 116 Name string 117 } 118 119 type TestForHandlerWithObjectAndMeta4Res struct { 120 Id int 121 Name string 122 } 123 124 type ControllerForHandlerWithObjectAndMeta2 struct{} 125 126 func (ControllerForHandlerWithObjectAndMeta2) Test3(ctx context.Context, req *TestForHandlerWithObjectAndMeta3Req) (res *TestForHandlerWithObjectAndMeta3Res, err error) { 127 return &TestForHandlerWithObjectAndMeta3Res{ 128 Id: 1, 129 Age: req.Age, 130 }, nil 131 } 132 133 func (ControllerForHandlerWithObjectAndMeta2) Test4(ctx context.Context, req *TestForHandlerWithObjectAndMeta4Req) (res *TestForHandlerWithObjectAndMeta4Res, err error) { 134 return &TestForHandlerWithObjectAndMeta4Res{ 135 Id: 1, 136 Name: req.Name, 137 }, nil 138 } 139 140 func Test_Router_Handler_Strict_WithObjectAndMeta(t *testing.T) { 141 s := g.Server(guid.S()) 142 s.Use(ghttp.MiddlewareHandlerResponse) 143 s.Group("/", func(group *ghttp.RouterGroup) { 144 group.ALL("/", new(ControllerForHandlerWithObjectAndMeta1)) 145 }) 146 s.SetDumpRouterMap(false) 147 s.Start() 148 defer s.Shutdown() 149 150 time.Sleep(100 * time.Millisecond) 151 gtest.C(t, func(t *gtest.T) { 152 client := g.Client() 153 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 154 155 t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`) 156 t.Assert(client.GetContent(ctx, "/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 157 t.Assert(client.GetContent(ctx, "/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 158 t.Assert(client.PostContent(ctx, "/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`) 159 }) 160 } 161 162 func Test_Router_Handler_Strict_Group_Bind(t *testing.T) { 163 s := g.Server(guid.S()) 164 s.Use(ghttp.MiddlewareHandlerResponse) 165 s.Group("/api/v1", func(group *ghttp.RouterGroup) { 166 group.Bind( 167 new(ControllerForHandlerWithObjectAndMeta1), 168 new(ControllerForHandlerWithObjectAndMeta2), 169 ) 170 }) 171 s.Group("/api/v2", func(group *ghttp.RouterGroup) { 172 group.Bind( 173 new(ControllerForHandlerWithObjectAndMeta1), 174 new(ControllerForHandlerWithObjectAndMeta2), 175 ) 176 }) 177 s.SetDumpRouterMap(false) 178 s.Start() 179 defer s.Shutdown() 180 181 time.Sleep(100 * time.Millisecond) 182 gtest.C(t, func(t *gtest.T) { 183 client := g.Client() 184 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 185 186 t.Assert(client.GetContent(ctx, "/"), `{"code":65,"message":"Not Found","data":null}`) 187 t.Assert(client.GetContent(ctx, "/api/v1/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 188 t.Assert(client.GetContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 189 t.Assert(client.PostContent(ctx, "/api/v1/custom-test2?age=18&name=john"), `{"code":65,"message":"Not Found","data":null}`) 190 191 t.Assert(client.GetContent(ctx, "/api/v1/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 192 t.Assert(client.GetContent(ctx, "/api/v1/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 193 194 t.Assert(client.GetContent(ctx, "/api/v2/custom-test1?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 195 t.Assert(client.GetContent(ctx, "/api/v2/custom-test2?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 196 t.Assert(client.GetContent(ctx, "/api/v2/custom-test3?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Age":18}}`) 197 t.Assert(client.GetContent(ctx, "/api/v2/custom-test4?age=18&name=john"), `{"code":0,"message":"","data":{"Id":1,"Name":"john"}}`) 198 }) 199 } 200 201 func Test_Issue1708(t *testing.T) { 202 type Test struct { 203 Name string `json:"name"` 204 } 205 type Req struct { 206 Page int `json:"page" dc:"分页码"` 207 Size int `json:"size" dc:"分页数量"` 208 TargetType string `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"` 209 TargetId uint `json:"targetId" v:"required#评论目标ID错误" dc:"对应内容ID"` 210 Test [][]Test `json:"test"` 211 } 212 type Res struct { 213 Page int `json:"page" dc:"分页码"` 214 Size int `json:"size" dc:"分页数量"` 215 TargetType string `json:"targetType" v:"required#评论内容类型错误" dc:"评论类型: topic/ask/article/reply"` 216 TargetId uint `json:"targetId" v:"required#评论目标ID错误" dc:"对应内容ID"` 217 Test [][]Test `json:"test"` 218 } 219 220 s := g.Server(guid.S()) 221 s.Use(ghttp.MiddlewareHandlerResponse) 222 s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) { 223 return &Res{ 224 Page: req.Page, 225 Size: req.Size, 226 TargetType: req.TargetType, 227 TargetId: req.TargetId, 228 Test: req.Test, 229 }, nil 230 }) 231 s.SetDumpRouterMap(false) 232 s.Start() 233 defer s.Shutdown() 234 235 time.Sleep(100 * time.Millisecond) 236 gtest.C(t, func(t *gtest.T) { 237 client := g.Client() 238 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 239 content := ` 240 { 241 "targetType":"topic", 242 "targetId":10785, 243 "test":[ 244 [ 245 { 246 "name":"123" 247 } 248 ] 249 ] 250 } 251 ` 252 t.Assert( 253 client.PostContent(ctx, "/test", content), 254 `{"code":0,"message":"","data":{"page":0,"size":0,"targetType":"topic","targetId":10785,"test":[[{"name":"123"}]]}}`, 255 ) 256 }) 257 } 258 259 func Test_Custom_Slice_Type_Attribute(t *testing.T) { 260 type ( 261 WhiteListKey string 262 WhiteListValues []string 263 WhiteList map[WhiteListKey]WhiteListValues 264 ) 265 type Req struct { 266 Id int 267 List WhiteList 268 } 269 type Res struct { 270 Content string 271 } 272 273 s := g.Server(guid.S()) 274 s.Use(ghttp.MiddlewareHandlerResponse) 275 s.BindHandler("/test", func(ctx context.Context, req *Req) (res *Res, err error) { 276 return &Res{ 277 Content: gjson.MustEncodeString(req), 278 }, nil 279 }) 280 s.SetDumpRouterMap(false) 281 s.Start() 282 defer s.Shutdown() 283 284 time.Sleep(100 * time.Millisecond) 285 gtest.C(t, func(t *gtest.T) { 286 client := g.Client() 287 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 288 content := ` 289 { 290 "id":1, 291 "list":{ 292 "key": ["a", "b", "c"] 293 } 294 } 295 ` 296 t.Assert( 297 client.PostContent(ctx, "/test", content), 298 `{"code":0,"message":"","data":{"Content":"{\"Id\":1,\"List\":{\"key\":[\"a\",\"b\",\"c\"]}}"}}`, 299 ) 300 }) 301 } 302 303 func Test_Router_Handler_Strict_WithGeneric(t *testing.T) { 304 type TestReq struct { 305 Age int 306 } 307 type TestGeneric[T any] struct { 308 Test T 309 } 310 type Test1Res struct { 311 Age TestGeneric[int] 312 } 313 type Test2Res TestGeneric[int] 314 type TestGenericRes[T any] struct { 315 Test T 316 } 317 318 s := g.Server(guid.S()) 319 s.Use(ghttp.MiddlewareHandlerResponse) 320 s.BindHandler("/test1", func(ctx context.Context, req *TestReq) (res *Test1Res, err error) { 321 return &Test1Res{ 322 Age: TestGeneric[int]{ 323 Test: req.Age, 324 }, 325 }, nil 326 }) 327 s.BindHandler("/test1_slice", func(ctx context.Context, req *TestReq) (res []Test1Res, err error) { 328 return []Test1Res{ 329 Test1Res{ 330 Age: TestGeneric[int]{ 331 Test: req.Age, 332 }, 333 }, 334 }, nil 335 }) 336 s.BindHandler("/test2", func(ctx context.Context, req *TestReq) (res *Test2Res, err error) { 337 return &Test2Res{ 338 Test: req.Age, 339 }, nil 340 }) 341 342 s.BindHandler("/test2_slice", func(ctx context.Context, req *TestReq) (res []Test2Res, err error) { 343 return []Test2Res{ 344 Test2Res{ 345 Test: req.Age, 346 }, 347 }, nil 348 }) 349 350 s.BindHandler("/test3", func(ctx context.Context, req *TestReq) (res *TestGenericRes[int], err error) { 351 return &TestGenericRes[int]{ 352 Test: req.Age, 353 }, nil 354 }) 355 356 s.SetDumpRouterMap(false) 357 s.Start() 358 defer s.Shutdown() 359 360 s.BindHandler("/test3_slice", func(ctx context.Context, req *TestReq) (res []TestGenericRes[int], err error) { 361 return []TestGenericRes[int]{ 362 TestGenericRes[int]{ 363 Test: req.Age, 364 }, 365 }, nil 366 }) 367 368 time.Sleep(100 * time.Millisecond) 369 gtest.C(t, func(t *gtest.T) { 370 client := g.Client() 371 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 372 373 t.Assert(client.GetContent(ctx, "/test1?age=1"), `{"code":0,"message":"","data":{"Age":{"Test":1}}}`) 374 t.Assert(client.GetContent(ctx, "/test1_slice?age=1"), `{"code":0,"message":"","data":[{"Age":{"Test":1}}]}`) 375 t.Assert(client.GetContent(ctx, "/test2?age=2"), `{"code":0,"message":"","data":{"Test":2}}`) 376 t.Assert(client.GetContent(ctx, "/test2_slice?age=2"), `{"code":0,"message":"","data":[{"Test":2}]}`) 377 t.Assert(client.GetContent(ctx, "/test3?age=3"), `{"code":0,"message":"","data":{"Test":3}}`) 378 t.Assert(client.GetContent(ctx, "/test3_slice?age=3"), `{"code":0,"message":"","data":[{"Test":3}]}`) 379 }) 380 } 381 382 type ParameterCaseSensitiveController struct{} 383 384 type ParameterCaseSensitiveControllerPathReq struct { 385 g.Meta `path:"/api/*path" method:"post"` 386 Path string 387 } 388 389 type ParameterCaseSensitiveControllerPathRes struct { 390 Path string 391 } 392 393 func (c *ParameterCaseSensitiveController) Path( 394 ctx context.Context, 395 req *ParameterCaseSensitiveControllerPathReq, 396 ) (res *ParameterCaseSensitiveControllerPathRes, err error) { 397 return &ParameterCaseSensitiveControllerPathRes{Path: req.Path}, nil 398 } 399 400 func Test_Router_Handler_Strict_ParameterCaseSensitive(t *testing.T) { 401 s := g.Server(guid.S()) 402 s.Use(ghttp.MiddlewareHandlerResponse) 403 s.Group("/", func(group *ghttp.RouterGroup) { 404 group.Bind(&ParameterCaseSensitiveController{}) 405 }) 406 s.SetDumpRouterMap(false) 407 s.Start() 408 defer s.Shutdown() 409 410 time.Sleep(100 * time.Millisecond) 411 gtest.C(t, func(t *gtest.T) { 412 client := g.Client() 413 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 414 415 for i := 0; i < 1000; i++ { 416 t.Assert( 417 client.PostContent(ctx, "/api/111", `{"Path":"222"}`), 418 `{"code":0,"message":"","data":{"Path":"222"}}`, 419 ) 420 } 421 }) 422 } 423 424 type testJsonRawMessageIssue3449Req struct { 425 g.Meta `path:"/test" method:"POST" sm:"hello" tags:"示例"` 426 427 Name string `json:"name" v:"required" dc:"名称"` 428 JSONRaw json.RawMessage `json:"jsonRaw" dc:"原始JSON"` 429 } 430 type testJsonRawMessageIssue3449Res struct { 431 Name string `json:"name" v:"required" dc:"名称"` 432 JSONRaw json.RawMessage `json:"jsonRaw" dc:"原始JSON"` 433 } 434 435 type testJsonRawMessageIssue3449 struct { 436 } 437 438 func (t *testJsonRawMessageIssue3449) Test(ctx context.Context, req *testJsonRawMessageIssue3449Req) (res *testJsonRawMessageIssue3449Res, err error) { 439 return &testJsonRawMessageIssue3449Res{ 440 Name: req.Name, 441 JSONRaw: req.JSONRaw, 442 }, nil 443 } 444 445 // https://github.com/gogf/gf/issues/3449 446 func Test_JsonRawMessage_Issue3449(t *testing.T) { 447 448 s := g.Server(guid.S()) 449 s.Use(ghttp.MiddlewareHandlerResponse) 450 s.Group("/", func(group *ghttp.RouterGroup) { 451 group.Bind(new(testJsonRawMessageIssue3449)) 452 }) 453 s.SetDumpRouterMap(false) 454 s.Start() 455 defer s.Shutdown() 456 457 time.Sleep(100 * time.Millisecond) 458 gtest.C(t, func(t *gtest.T) { 459 client := g.Client() 460 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 461 v1 := map[string]any{ 462 "jkey1": "11", 463 "jkey2": "12", 464 } 465 466 v2 := map[string]any{ 467 "jkey1": "21", 468 "jkey2": "22", 469 } 470 data := map[string]any{ 471 "Name": "test", 472 "jsonRaw": []any{ 473 v1, v2, 474 }, 475 } 476 477 expect1 := `{"code":0,"message":"","data":{"name":"test","jsonRaw":[{"jkey1":"11","jkey2":"12"},{"jkey1":"21","jkey2":"22"}]}}` 478 t.Assert(client.PostContent(ctx, "/test", data), expect1) 479 480 expect2 := `{"code":0,"message":"","data":{"name":"test","jsonRaw":{"jkey1":"11","jkey2":"12"}}}` 481 t.Assert(client.PostContent(ctx, "/test", map[string]any{ 482 "Name": "test", 483 "jsonRaw": v1, 484 }), expect2) 485 486 }) 487 } 488 489 type testNullStringIssue3465Req struct { 490 g.Meta `path:"/test" method:"get" sm:"hello" tags:"示例"` 491 Name []string `json:"name" v:"required"` 492 } 493 type testNullStringIssue3465Res struct { 494 Name []string `json:"name" v:"required" ` 495 } 496 497 type testNullStringIssue3465 struct { 498 } 499 500 func (t *testNullStringIssue3465) Test(ctx context.Context, req *testNullStringIssue3465Req) (res *testNullStringIssue3465Res, err error) { 501 return &testNullStringIssue3465Res{ 502 Name: req.Name, 503 }, nil 504 } 505 506 // https://github.com/gogf/gf/issues/3465 507 func Test_NullString_Issue3465(t *testing.T) { 508 509 s := g.Server(guid.S()) 510 s.Use(ghttp.MiddlewareHandlerResponse) 511 s.Group("/", func(group *ghttp.RouterGroup) { 512 group.Bind(new(testNullStringIssue3465)) 513 }) 514 s.SetDumpRouterMap(false) 515 s.Start() 516 defer s.Shutdown() 517 518 time.Sleep(100 * time.Millisecond) 519 gtest.C(t, func(t *gtest.T) { 520 client := g.Client() 521 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 522 523 data1 := map[string]any{ 524 "name": "null", 525 } 526 527 expect1 := `{"code":0,"message":"","data":{"name":["null"]}}` 528 t.Assert(client.GetContent(ctx, "/test", data1), expect1) 529 530 data2 := map[string]any{ 531 "name": []string{"null", "null"}, 532 } 533 expect2 := `{"code":0,"message":"","data":{"name":["null","null"]}}` 534 t.Assert(client.GetContent(ctx, "/test", data2), expect2) 535 536 }) 537 }