github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_unit_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/gogf/gf. 6 7 package ghttp_test 8 9 import ( 10 "fmt" 11 "testing" 12 "time" 13 14 "github.com/gogf/gf/util/gvalid" 15 16 "github.com/gogf/gf/frame/g" 17 "github.com/gogf/gf/net/ghttp" 18 "github.com/gogf/gf/test/gtest" 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 p, _ := ports.PopRand() 28 s := g.Server(p) 29 s.BindHandler("/parse", func(r *ghttp.Request) { 30 var user *User 31 if err := r.Parse(&user); err != nil { 32 r.Response.WriteExit(err) 33 } 34 r.Response.WriteExit(user.Map["id"], user.Map["score"]) 35 }) 36 s.SetPort(p) 37 s.SetDumpRouterMap(false) 38 s.Start() 39 defer s.Shutdown() 40 41 time.Sleep(100 * time.Millisecond) 42 gtest.C(t, func(t *gtest.T) { 43 client := g.Client() 44 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 45 t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","map":{"id":1,"score":100}}`), `1100`) 46 }) 47 } 48 49 func Test_Params_ParseQuery(t *testing.T) { 50 type User struct { 51 Id int 52 Name string 53 } 54 p, _ := ports.PopRand() 55 s := g.Server(p) 56 s.BindHandler("/parse-query", func(r *ghttp.Request) { 57 var user *User 58 if err := r.ParseQuery(&user); err != nil { 59 r.Response.WriteExit(err) 60 } 61 r.Response.WriteExit(user.Id, user.Name) 62 }) 63 s.SetPort(p) 64 s.SetDumpRouterMap(false) 65 s.Start() 66 defer s.Shutdown() 67 68 time.Sleep(100 * time.Millisecond) 69 gtest.C(t, func(t *gtest.T) { 70 c := g.Client() 71 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 72 t.Assert(c.GetContent("/parse-query"), `0`) 73 t.Assert(c.GetContent("/parse-query?id=1&name=john"), `1john`) 74 t.Assert(c.PostContent("/parse-query"), `0`) 75 t.Assert(c.PostContent("/parse-query", g.Map{ 76 "id": 1, 77 "name": "john", 78 }), `0`) 79 }) 80 } 81 82 func Test_Params_ParseForm(t *testing.T) { 83 type User struct { 84 Id int 85 Name string 86 } 87 p, _ := ports.PopRand() 88 s := g.Server(p) 89 s.BindHandler("/parse-form", func(r *ghttp.Request) { 90 var user *User 91 if err := r.ParseForm(&user); err != nil { 92 r.Response.WriteExit(err) 93 } 94 r.Response.WriteExit(user.Id, user.Name) 95 }) 96 s.SetPort(p) 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", p)) 105 t.Assert(c.GetContent("/parse-form"), `0`) 106 t.Assert(c.GetContent("/parse-form", g.Map{ 107 "id": 1, 108 "name": "john", 109 }), 0) 110 t.Assert(c.PostContent("/parse-form"), `0`) 111 t.Assert(c.PostContent("/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 p, _ := ports.PopRand() 187 s := g.Server(p) 188 s.BindHandler("/parse", func(r *ghttp.Request) { 189 if m := r.GetMap(); len(m) > 0 { 190 var data *SaveRequest 191 if err := r.Parse(&data); err != nil { 192 r.Response.WriteExit(err) 193 } 194 r.Response.WriteExit(data) 195 } 196 }) 197 s.SetPort(p) 198 s.SetDumpRouterMap(false) 199 s.Start() 200 defer s.Shutdown() 201 202 time.Sleep(100 * time.Millisecond) 203 gtest.C(t, func(t *gtest.T) { 204 client := g.Client() 205 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 206 content := ` 207 { 208 "app_id": 5, 209 "cluster": "test", 210 "container_image": "nginx", 211 "container_name": "test", 212 "container_ports": [ 213 { 214 "alias": "别名", 215 "brief": "描述", 216 "port": 80, 217 "type": "tcp" 218 } 219 ], 220 "cpu_limit": 100, 221 "cpu_request": 10, 222 "create_at": "2020-10-10 12:00:00", 223 "creator": 1, 224 "env_container": [ 225 { 226 "brief": "用户环境变量", 227 "key": "NAME", 228 "type": "string", 229 "value": "john" 230 } 231 ], 232 "env_global": [ 233 { 234 "brief": "数据数量", 235 "key": "NUMBER", 236 "type": "string", 237 "value": "1" 238 } 239 ], 240 "env_reserve": [ 241 { 242 "key": "NODE_IP", 243 "value": "status.hostIP" 244 } 245 ], 246 "liveness_probe": { 247 "brief": "存活探针", 248 "initial_delay": 10, 249 "path": "", 250 "period": 5, 251 "port": 80, 252 "type": "tcpSocket" 253 }, 254 "readiness_probe": { 255 "brief": "就绪探针", 256 "initial_delay": 10, 257 "path": "", 258 "period": 5, 259 "port": 80, 260 "type": "tcpSocket" 261 }, 262 "id": 0, 263 "init_image": "", 264 "labels": [ 265 { 266 "key": "app", 267 "value": "test" 268 } 269 ], 270 "mem_limit": 1000, 271 "mem_request": 100, 272 "mesh_enabled": 0, 273 "metrics": "", 274 "mounts": [], 275 "name": "test", 276 "namespace": "test", 277 "node_selector": [ 278 { 279 "key": "group", 280 "value": "app" 281 } 282 ], 283 "replicas": 1, 284 "type": "test", 285 "update_at": "2020-10-10 12:00:00", 286 "version_tag": "test" 287 } 288 ` 289 t.Assert(client.PostContent("/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}}`) 290 }) 291 } 292 293 func Test_Params_Parse_Attr_Pointer1(t *testing.T) { 294 type User struct { 295 Id *int 296 Name *string 297 } 298 p, _ := ports.PopRand() 299 s := g.Server(p) 300 s.BindHandler("/parse1", func(r *ghttp.Request) { 301 if m := r.GetMap(); len(m) > 0 { 302 var user *User 303 if err := r.Parse(&user); err != nil { 304 r.Response.WriteExit(err) 305 } 306 r.Response.WriteExit(user.Id, user.Name) 307 } 308 }) 309 s.BindHandler("/parse2", func(r *ghttp.Request) { 310 if m := r.GetMap(); len(m) > 0 { 311 var user = new(User) 312 if err := r.Parse(user); err != nil { 313 r.Response.WriteExit(err) 314 } 315 r.Response.WriteExit(user.Id, user.Name) 316 } 317 }) 318 s.SetPort(p) 319 s.SetDumpRouterMap(false) 320 s.Start() 321 defer s.Shutdown() 322 323 time.Sleep(100 * time.Millisecond) 324 gtest.C(t, func(t *gtest.T) { 325 client := g.Client() 326 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 327 t.Assert(client.PostContent("/parse1", `{"id":1,"name":"john"}`), `1john`) 328 t.Assert(client.PostContent("/parse2", `{"id":1,"name":"john"}`), `1john`) 329 t.Assert(client.PostContent("/parse2?id=1&name=john"), `1john`) 330 t.Assert(client.PostContent("/parse2", `id=1&name=john`), `1john`) 331 }) 332 } 333 334 func Test_Params_Parse_Attr_Pointer2(t *testing.T) { 335 type User struct { 336 Id *int `v:"required"` 337 } 338 p, _ := ports.PopRand() 339 s := g.Server(p) 340 s.BindHandler("/parse", func(r *ghttp.Request) { 341 var user *User 342 if err := r.Parse(&user); err != nil { 343 r.Response.WriteExit(err.Error()) 344 } 345 r.Response.WriteExit(user.Id) 346 }) 347 s.SetPort(p) 348 s.SetDumpRouterMap(false) 349 s.Start() 350 defer s.Shutdown() 351 352 time.Sleep(100 * time.Millisecond) 353 gtest.C(t, func(t *gtest.T) { 354 client := g.Client() 355 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 356 t.Assert(client.PostContent("/parse"), `The Id field is required`) 357 t.Assert(client.PostContent("/parse?id=1"), `1`) 358 }) 359 } 360 361 // It does not support this kind of converting yet. 362 //func Test_Params_Parse_Attr_SliceSlice(t *testing.T) { 363 // type User struct { 364 // Id int 365 // Name string 366 // Scores [][]int 367 // } 368 // p, _ := ports.PopRand() 369 // s := g.Server(p) 370 // s.BindHandler("/parse", func(r *ghttp.Request) { 371 // if m := r.GetMap(); len(m) > 0 { 372 // var user *User 373 // if err := r.Parse(&user); err != nil { 374 // r.Response.WriteExit(err) 375 // } 376 // r.Response.WriteExit(user.Scores) 377 // } 378 // }) 379 // s.SetPort(p) 380 // s.SetDumpRouterMap(false) 381 // s.Start() 382 // defer s.Shutdown() 383 // 384 // time.Sleep(100 * time.Millisecond) 385 // gtest.C(t, func(t *gtest.T) { 386 // client := g.Client() 387 // client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 388 // t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","scores":[[1,2,3]]}`), `1100`) 389 // }) 390 //} 391 392 func Test_Params_Struct(t *testing.T) { 393 type User struct { 394 Id int 395 Name string 396 Time *time.Time 397 Pass1 string `p:"password1"` 398 Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` 399 } 400 p, _ := ports.PopRand() 401 s := g.Server(p) 402 s.BindHandler("/struct1", func(r *ghttp.Request) { 403 if m := r.GetMap(); len(m) > 0 { 404 user := new(User) 405 if err := r.GetStruct(user); err != nil { 406 r.Response.WriteExit(err) 407 } 408 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 409 } 410 }) 411 s.BindHandler("/struct2", func(r *ghttp.Request) { 412 if m := r.GetMap(); len(m) > 0 { 413 user := (*User)(nil) 414 if err := r.GetStruct(&user); err != nil { 415 r.Response.WriteExit(err) 416 } 417 if user != nil { 418 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 419 } 420 } 421 }) 422 s.BindHandler("/struct-valid", func(r *ghttp.Request) { 423 if m := r.GetMap(); len(m) > 0 { 424 user := new(User) 425 if err := r.GetStruct(user); err != nil { 426 r.Response.WriteExit(err) 427 } 428 if err := gvalid.CheckStruct(r.Context(), user, nil); err != nil { 429 r.Response.WriteExit(err) 430 } 431 } 432 }) 433 s.BindHandler("/parse", func(r *ghttp.Request) { 434 if m := r.GetMap(); len(m) > 0 { 435 var user *User 436 if err := r.Parse(&user); err != nil { 437 r.Response.WriteExit(err) 438 } 439 r.Response.WriteExit(user.Id, user.Name, user.Pass1, user.Pass2) 440 } 441 }) 442 s.SetPort(p) 443 s.SetDumpRouterMap(false) 444 s.Start() 445 defer s.Shutdown() 446 447 time.Sleep(100 * time.Millisecond) 448 gtest.C(t, func(t *gtest.T) { 449 client := g.Client() 450 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 451 t.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) 452 t.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`) 453 t.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`) 454 t.Assert(client.PostContent("/struct2", ``), ``) 455 t.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`) 456 t.Assert(client.PostContent("/parse", `id=1&name=john&password1=123&password2=0`), `The password2 value length must be between 2 and 20; 密码强度不足`) 457 t.Assert(client.PostContent("/parse", `{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}`), `1john123Abc!@#123Abc!@#`) 458 }) 459 } 460 461 func Test_Params_Structs(t *testing.T) { 462 type User struct { 463 Id int 464 Name string 465 Time *time.Time 466 Pass1 string `p:"password1"` 467 Pass2 string `p:"password2" v:"password2 @required|length:2,20|password3#||密码强度不足"` 468 } 469 p, _ := ports.PopRand() 470 s := g.Server(p) 471 s.BindHandler("/parse1", func(r *ghttp.Request) { 472 var users []*User 473 if err := r.Parse(&users); err != nil { 474 r.Response.WriteExit(err) 475 } 476 r.Response.WriteExit(users[0].Id, users[1].Id) 477 }) 478 s.SetPort(p) 479 s.SetDumpRouterMap(false) 480 s.Start() 481 defer s.Shutdown() 482 483 time.Sleep(100 * time.Millisecond) 484 gtest.C(t, func(t *gtest.T) { 485 client := g.Client() 486 client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 487 t.Assert(client.PostContent( 488 "/parse1", 489 `[{"id":1,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}, {"id":2,"name":"john","password1":"123Abc!@#","password2":"123Abc!@#"}]`), 490 `12`, 491 ) 492 }) 493 } 494 495 func Test_Params_Struct_Validation(t *testing.T) { 496 type User struct { 497 Id int `v:"required"` 498 Name string `v:"name@required-with:id"` 499 } 500 p, _ := ports.PopRand() 501 s := g.Server(p) 502 s.Group("/", func(group *ghttp.RouterGroup) { 503 group.ALL("/", func(r *ghttp.Request) { 504 var ( 505 err error 506 user *User 507 ) 508 err = r.Parse(&user) 509 if err != nil { 510 r.Response.WriteExit(err) 511 } 512 r.Response.WriteExit(user.Id, user.Name) 513 }) 514 }) 515 s.SetPort(p) 516 s.SetDumpRouterMap(false) 517 s.Start() 518 defer s.Shutdown() 519 520 time.Sleep(100 * time.Millisecond) 521 gtest.C(t, func(t *gtest.T) { 522 c := g.Client() 523 c.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p)) 524 t.Assert(c.GetContent("/", ``), `The Id field is required`) 525 t.Assert(c.GetContent("/", `id=1&name=john`), `1john`) 526 t.Assert(c.PostContent("/", `id=1&name=john&password1=123&password2=456`), `1john`) 527 t.Assert(c.PostContent("/", `id=1`), `The name field is required`) 528 }) 529 }