github.com/wangyougui/gf/v2@v2.6.5/net/ghttp/ghttp_z_unit_feature_request_struct_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 "fmt" 11 "testing" 12 "time" 13 14 "github.com/wangyougui/gf/v2/frame/g" 15 "github.com/wangyougui/gf/v2/net/ghttp" 16 "github.com/wangyougui/gf/v2/test/gtest" 17 "github.com/wangyougui/gf/v2/util/guid" 18 "github.com/wangyougui/gf/v2/util/gvalid" 19 ) 20 21 func Test_Params_Parse(t *testing.T) { 22 type User struct { 23 Id int 24 Name string 25 Map map[string]interface{} 26 } 27 s := g.Server(guid.S()) 28 s.BindHandler("/parse", func(r *ghttp.Request) { 29 var user *User 30 if err := r.Parse(&user); err != nil { 31 r.Response.WriteExit(err) 32 } 33 r.Response.WriteExit(user.Map["id"], user.Map["score"]) 34 }) 35 s.BindHandler("/parseErr", func(r *ghttp.Request) { 36 var user User 37 err := r.Parse(user) 38 r.Response.WriteExit(err != nil) 39 }) 40 s.SetDumpRouterMap(false) 41 s.Start() 42 defer s.Shutdown() 43 44 time.Sleep(100 * time.Millisecond) 45 gtest.C(t, func(t *gtest.T) { 46 client := g.Client() 47 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 48 t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`) 49 t.Assert(client.PostContent(ctx, "/parseErr", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), true) 50 }) 51 } 52 53 func Test_Params_ParseQuery(t *testing.T) { 54 type User struct { 55 Id int 56 Name string 57 } 58 s := g.Server(guid.S()) 59 s.BindHandler("/parse-query", func(r *ghttp.Request) { 60 var user *User 61 if err := r.ParseQuery(&user); err != nil { 62 r.Response.WriteExit(err) 63 } 64 r.Response.WriteExit(user.Id, user.Name) 65 }) 66 s.SetDumpRouterMap(false) 67 s.Start() 68 defer s.Shutdown() 69 70 time.Sleep(100 * time.Millisecond) 71 gtest.C(t, func(t *gtest.T) { 72 c := g.Client() 73 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 74 t.Assert(c.GetContent(ctx, "/parse-query"), `0`) 75 t.Assert(c.GetContent(ctx, "/parse-query?id=1&name=john"), `1john`) 76 t.Assert(c.PostContent(ctx, "/parse-query"), `0`) 77 t.Assert(c.PostContent(ctx, "/parse-query", g.Map{ 78 "id": 1, 79 "name": "john", 80 }), `0`) 81 }) 82 } 83 84 func Test_Params_ParseForm(t *testing.T) { 85 type User struct { 86 Id int 87 Name string 88 } 89 s := g.Server(guid.S()) 90 s.BindHandler("/parse-form", func(r *ghttp.Request) { 91 var user *User 92 if err := r.ParseForm(&user); err != nil { 93 r.Response.WriteExit(err) 94 } 95 r.Response.WriteExit(user.Id, user.Name) 96 }) 97 s.SetDumpRouterMap(false) 98 s.Start() 99 defer s.Shutdown() 100 101 time.Sleep(100 * time.Millisecond) 102 gtest.C(t, func(t *gtest.T) { 103 c := g.Client() 104 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 105 t.Assert(c.GetContent(ctx, "/parse-form"), `0`) 106 t.Assert(c.GetContent(ctx, "/parse-form", g.Map{ 107 "id": 1, 108 "name": "john", 109 }), 0) 110 t.Assert(c.PostContent(ctx, "/parse-form"), `0`) 111 t.Assert(c.PostContent(ctx, "/parse-form", g.Map{ 112 "id": 1, 113 "name": "john", 114 }), `1john`) 115 }) 116 } 117 118 func Test_Params_ComplexJsonStruct(t *testing.T) { 119 type ItemEnv struct { 120 Type string 121 Key string 122 Value string 123 Brief string 124 } 125 126 type ItemProbe struct { 127 Type string 128 Port int 129 Path string 130 Brief string 131 Period int 132 InitialDelay int 133 TimeoutSeconds int 134 } 135 136 type ItemKV struct { 137 Key string 138 Value string 139 } 140 141 type ItemPort struct { 142 Port int 143 Type string 144 Alias string 145 Brief string 146 } 147 148 type ItemMount struct { 149 Type string 150 DstPath string 151 Src string 152 SrcPath string 153 Brief string 154 } 155 156 type SaveRequest struct { 157 AppId uint 158 Name string 159 Type string 160 Cluster string 161 Replicas uint 162 ContainerName string 163 ContainerImage string 164 VersionTag string 165 Namespace string 166 Id uint 167 Status uint 168 Metrics string 169 InitImage string 170 CpuRequest uint 171 CpuLimit uint 172 MemRequest uint 173 MemLimit uint 174 MeshEnabled uint 175 ContainerPorts []ItemPort 176 Labels []ItemKV 177 NodeSelector []ItemKV 178 EnvReserve []ItemKV 179 EnvGlobal []ItemEnv 180 EnvContainer []ItemEnv 181 Mounts []ItemMount 182 LivenessProbe ItemProbe 183 ReadinessProbe ItemProbe 184 } 185 186 s := g.Server(guid.S()) 187 s.BindHandler("/parse", func(r *ghttp.Request) { 188 if m := r.GetMap(); len(m) > 0 { 189 var data *SaveRequest 190 if err := r.Parse(&data); err != nil { 191 r.Response.WriteExit(err) 192 } 193 r.Response.WriteExit(data) 194 } 195 }) 196 s.SetDumpRouterMap(false) 197 s.Start() 198 defer s.Shutdown() 199 200 time.Sleep(100 * time.Millisecond) 201 gtest.C(t, func(t *gtest.T) { 202 client := g.Client() 203 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 204 content := ` 205 { 206 "app_id": 5, 207 "cluster": "test", 208 "container_image": "nginx", 209 "container_name": "test", 210 "container_ports": [ 211 { 212 "alias": "别名", 213 "brief": "描述", 214 "port": 80, 215 "type": "tcp" 216 } 217 ], 218 "cpu_limit": 100, 219 "cpu_request": 10, 220 "create_at": "2020-10-10 12:00:00", 221 "creator": 1, 222 "env_container": [ 223 { 224 "brief": "用户环境变量", 225 "key": "NAME", 226 "type": "string", 227 "value": "john" 228 } 229 ], 230 "env_global": [ 231 { 232 "brief": "数据数量", 233 "key": "NUMBER", 234 "type": "string", 235 "value": "1" 236 } 237 ], 238 "env_reserve": [ 239 { 240 "key": "NODE_IP", 241 "value": "status.hostIP" 242 } 243 ], 244 "liveness_probe": { 245 "brief": "存活探针", 246 "initial_delay": 10, 247 "path": "", 248 "period": 5, 249 "port": 80, 250 "type": "tcpSocket" 251 }, 252 "readiness_probe": { 253 "brief": "就绪探针", 254 "initial_delay": 10, 255 "path": "", 256 "period": 5, 257 "port": 80, 258 "type": "tcpSocket" 259 }, 260 "id": 0, 261 "init_image": "", 262 "labels": [ 263 { 264 "key": "app", 265 "value": "test" 266 } 267 ], 268 "mem_limit": 1000, 269 "mem_request": 100, 270 "mesh_enabled": 0, 271 "metrics": "", 272 "mounts": [], 273 "name": "test", 274 "namespace": "test", 275 "node_selector": [ 276 { 277 "key": "group", 278 "value": "app" 279 } 280 ], 281 "replicas": 1, 282 "type": "test", 283 "update_at": "2020-10-10 12:00:00", 284 "version_tag": "test" 285 } 286 ` 287 t.Assert(client.PostContent(ctx, "/parse", content), `{"AppId":5,"Name":"test","Type":"test","Cluster":"test","Replicas":1,"ContainerName":"test","ContainerImage":"nginx","VersionTag":"test","Namespace":"test","Id":0,"Status":0,"Metrics":"","InitImage":"","CpuRequest":10,"CpuLimit":100,"MemRequest":100,"MemLimit":1000,"MeshEnabled":0,"ContainerPorts":[{"Port":80,"Type":"tcp","Alias":"别名","Brief":"描述"}],"Labels":[{"Key":"app","Value":"test"}],"NodeSelector":[{"Key":"group","Value":"app"}],"EnvReserve":[{"Key":"NODE_IP","Value":"status.hostIP"}],"EnvGlobal":[{"Type":"string","Key":"NUMBER","Value":"1","Brief":"数据数量"}],"EnvContainer":[{"Type":"string","Key":"NAME","Value":"john","Brief":"用户环境变量"}],"Mounts":[],"LivenessProbe":{"Type":"tcpSocket","Port":80,"Path":"","Brief":"存活探针","Period":5,"InitialDelay":10,"TimeoutSeconds":0},"ReadinessProbe":{"Type":"tcpSocket","Port":80,"Path":"","Brief":"就绪探针","Period":5,"InitialDelay":10,"TimeoutSeconds":0}}`) 288 }) 289 } 290 291 func Test_Params_Parse_Attr_Pointer1(t *testing.T) { 292 type User struct { 293 Id *int 294 Name *string 295 } 296 s := g.Server(guid.S()) 297 s.BindHandler("/parse1", func(r *ghttp.Request) { 298 if m := r.GetMap(); len(m) > 0 { 299 var user *User 300 if err := r.Parse(&user); err != nil { 301 r.Response.WriteExit(err) 302 } 303 r.Response.WriteExit(user.Id, user.Name) 304 } 305 }) 306 s.BindHandler("/parse2", func(r *ghttp.Request) { 307 if m := r.GetMap(); len(m) > 0 { 308 var user = new(User) 309 if err := r.Parse(user); err != nil { 310 r.Response.WriteExit(err) 311 } 312 r.Response.WriteExit(user.Id, user.Name) 313 } 314 }) 315 s.SetDumpRouterMap(false) 316 s.Start() 317 defer s.Shutdown() 318 319 time.Sleep(100 * time.Millisecond) 320 gtest.C(t, func(t *gtest.T) { 321 client := g.Client() 322 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 323 t.Assert(client.PostContent(ctx, "/parse1", `{"id":1,"name":"john"}`), `1john`) 324 t.Assert(client.PostContent(ctx, "/parse2", `{"id":1,"name":"john"}`), `1john`) 325 t.Assert(client.PostContent(ctx, "/parse2?id=1&name=john"), `1john`) 326 t.Assert(client.PostContent(ctx, "/parse2", `id=1&name=john`), `1john`) 327 }) 328 } 329 330 func Test_Params_Parse_Attr_Pointer2(t *testing.T) { 331 type User struct { 332 Id *int `v:"required"` 333 } 334 s := g.Server(guid.S()) 335 s.BindHandler("/parse", func(r *ghttp.Request) { 336 var user *User 337 if err := r.Parse(&user); err != nil { 338 r.Response.WriteExit(err.Error()) 339 } 340 r.Response.WriteExit(user.Id) 341 }) 342 s.SetDumpRouterMap(false) 343 s.Start() 344 defer s.Shutdown() 345 346 time.Sleep(100 * time.Millisecond) 347 gtest.C(t, func(t *gtest.T) { 348 client := g.Client() 349 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 350 t.Assert(client.PostContent(ctx, "/parse"), `The Id field is required`) 351 t.Assert(client.PostContent(ctx, "/parse?id=1"), `1`) 352 }) 353 } 354 355 // It does not support this kind of converting yet. 356 // func Test_Params_Parse_Attr_SliceSlice(t *testing.T) { 357 // type User struct { 358 // Id int 359 // Name string 360 // Scores [][]int 361 // } 362 // // s := g.Server(guid.S()) 363 // s.BindHandler("/parse", func(r *ghttp.Request) { 364 // if m := r.GetMap(); len(m) > 0 { 365 // var user *User 366 // if err := r.Parse(&user); err != nil { 367 // r.Response.WriteExit(err) 368 // } 369 // r.Response.WriteExit(user.Scores) 370 // } 371 // }) 372 // // s.SetDumpRouterMap(false) 373 // s.Start() 374 // defer s.Shutdown() 375 // 376 // time.Sleep(100 * time.Millisecond) 377 // gtest.C(t, func(t *gtest.T) { 378 // client := g.Client() 379 // client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 380 // t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`) 381 // }) 382 // } 383 384 func Test_Params_Struct(t *testing.T) { 385 type User struct { 386 Id int 387 Name string 388 Time *time.Time 389 Pass1 string `p:"password1"` 390 Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` 391 } 392 s := g.Server(guid.S()) 393 s.BindHandler("/struct1", func(r *ghttp.Request) { 394 if m := r.GetMap(); len(m) > 0 { 395 user := new(User) 396 if err := r.GetStruct(user); err != nil { 397 r.Response.WriteExit(err) 398 } 399 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 400 } 401 }) 402 s.BindHandler("/struct2", func(r *ghttp.Request) { 403 if m := r.GetMap(); len(m) > 0 { 404 user := (*User)(nil) 405 if err := r.GetStruct(&user); err != nil { 406 r.Response.WriteExit(err) 407 } 408 if user != nil { 409 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 410 } 411 } 412 }) 413 s.BindHandler("/struct-valid", func(r *ghttp.Request) { 414 if m := r.GetMap(); len(m) > 0 { 415 user := new(User) 416 if err := r.GetStruct(user); err != nil { 417 r.Response.WriteExit(err) 418 } 419 if err := gvalid.New().Data(user).Run(r.Context()); err != nil { 420 r.Response.WriteExit(err) 421 } 422 } 423 }) 424 s.BindHandler("/parse", func(r *ghttp.Request) { 425 if m := r.GetMap(); len(m) > 0 { 426 var user *User 427 if err := r.Parse(&user); err != nil { 428 r.Response.WriteExit(err) 429 } 430 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 431 } 432 }) 433 s.SetDumpRouterMap(false) 434 s.Start() 435 defer s.Shutdown() 436 437 time.Sleep(100 * time.Millisecond) 438 gtest.C(t, func(t *gtest.T) { 439 client := g.Client() 440 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 441 t.Assert(client.GetContent(ctx, "/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) 442 t.Assert(client.PostContent(ctx, "/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) 443 t.Assert(client.PostContent(ctx, "/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`) 444 t.Assert(client.PostContent(ctx, "/struct2", ``), ``) 445 t.Assert(client.PostContent(ctx, "/struct-valid", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20; 密码强度不足") 446 t.Assert(client.PostContent(ctx, "/parse", `id=1&name=john&password1=123&password2=0`), "The password2 value `0` length must be between 2 and 20") 447 t.Assert(client.PostContent(ctx, "/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) 448 }) 449 } 450 451 func Test_Params_Structs(t *testing.T) { 452 type User struct { 453 Id int 454 Name string 455 Time *time.Time 456 Pass1 string `p:"password1"` 457 Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` 458 } 459 s := g.Server(guid.S()) 460 s.BindHandler("/parse1", func(r *ghttp.Request) { 461 var users []*User 462 if err := r.Parse(&users); err != nil { 463 r.Response.WriteExit(err) 464 } 465 r.Response.WriteExit(users[0].Id, users[1].Id) 466 }) 467 s.SetDumpRouterMap(false) 468 s.Start() 469 defer s.Shutdown() 470 471 time.Sleep(100 * time.Millisecond) 472 gtest.C(t, func(t *gtest.T) { 473 client := g.Client() 474 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 475 t.Assert(client.PostContent(ctx, 476 "/parse1", 477 `[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`), 478 `12`, 479 ) 480 }) 481 } 482 483 func Test_Params_Struct_Validation(t *testing.T) { 484 type User struct { 485 Id int `v:"required"` 486 Name string `v:"name@required-with:id"` 487 } 488 s := g.Server(guid.S()) 489 s.Group("/", func(group *ghttp.RouterGroup) { 490 group.ALL("/", func(r *ghttp.Request) { 491 var ( 492 err error 493 user *User 494 ) 495 err = r.Parse(&user) 496 if err != nil { 497 r.Response.WriteExit(err) 498 } 499 r.Response.WriteExit(user.Id, user.Name) 500 }) 501 }) 502 s.SetDumpRouterMap(false) 503 s.Start() 504 defer s.Shutdown() 505 506 time.Sleep(100 * time.Millisecond) 507 gtest.C(t, func(t *gtest.T) { 508 c := g.Client() 509 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 510 t.Assert(c.GetContent(ctx, "/", ``), `The Id field is required`) 511 t.Assert(c.GetContent(ctx, "/", `id=1&name=john`), `1john`) 512 t.Assert(c.PostContent(ctx, "/", `id=1&name=john&password1=123&password2=456`), `1john`) 513 t.Assert(c.PostContent(ctx, "/", `id=1`), `The name field is required`) 514 }) 515 } 516 517 // https://github.com/wangyougui/gf/issues/1488 518 func Test_Params_Parse_Issue1488(t *testing.T) { 519 s := g.Server(guid.S()) 520 s.Group("/", func(group *ghttp.RouterGroup) { 521 group.ALL("/", func(r *ghttp.Request) { 522 type Request struct { 523 Type []int `p:"type"` 524 Keyword string `p:"keyword"` 525 Limit int `p:"per_page" d:"10"` 526 Page int `p:"page" d:"1"` 527 Order string 528 CreatedAtLte string 529 CreatedAtGte string 530 CreatorID []int 531 } 532 for i := 0; i < 10; i++ { 533 r.SetParamMap(g.Map{ 534 "type[]": 0, 535 "keyword": "", 536 "t_start": "", 537 "t_end": "", 538 "reserve_at_start": "", 539 "reserve_at_end": "", 540 "user_name": "", 541 "flag": "", 542 "per_page": 6, 543 }) 544 var parsed Request 545 _ = r.Parse(&parsed) 546 r.Response.Write(parsed.Page, parsed.Limit) 547 } 548 }) 549 }) 550 s.SetDumpRouterMap(false) 551 s.Start() 552 defer s.Shutdown() 553 554 time.Sleep(100 * time.Millisecond) 555 gtest.C(t, func(t *gtest.T) { 556 c := g.Client() 557 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())) 558 t.Assert(c.GetContent(ctx, "/", ``), `16161616161616161616`) 559 }) 560 }