github.com/cloudwego/hertz@v0.9.3/pkg/app/server/binding/binder_test.go (about) 1 /* 2 * Copyright 2023 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * MIT License 16 * 17 * Copyright (c) 2019-present Fenny and Contributors 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining a copy 20 * of this software and associated documentation files (the "Software"), to deal 21 * in the Software without restriction, including without limitation the rights 22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 * copies of the Software, and to permit persons to whom the Software is 24 * furnished to do so, subject to the following conditions: 25 * 26 * The above copyright notice and this permission notice shall be included in all 27 * copies or substantial portions of the Software. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 * SOFTWARE. 36 * 37 * This file may have been modified by CloudWeGo authors. All CloudWeGo 38 * Modifications are Copyright 2023 CloudWeGo Authors 39 */ 40 41 package binding 42 43 import ( 44 "encoding/json" 45 "fmt" 46 "mime/multipart" 47 "net/url" 48 "reflect" 49 "testing" 50 "time" 51 52 "github.com/cloudwego/hertz/pkg/app/server/binding/testdata" 53 "github.com/cloudwego/hertz/pkg/common/test/assert" 54 "github.com/cloudwego/hertz/pkg/protocol" 55 "github.com/cloudwego/hertz/pkg/protocol/consts" 56 req2 "github.com/cloudwego/hertz/pkg/protocol/http1/req" 57 "github.com/cloudwego/hertz/pkg/route/param" 58 "google.golang.org/protobuf/proto" 59 ) 60 61 type mockRequest struct { 62 Req *protocol.Request 63 } 64 65 func newMockRequest() *mockRequest { 66 return &mockRequest{ 67 Req: &protocol.Request{}, 68 } 69 } 70 71 func (m *mockRequest) SetRequestURI(uri string) *mockRequest { 72 m.Req.SetRequestURI(uri) 73 return m 74 } 75 76 func (m *mockRequest) SetFile(param, fileName string) *mockRequest { 77 m.Req.SetFile(param, fileName) 78 return m 79 } 80 81 func (m *mockRequest) SetHeader(key, value string) *mockRequest { 82 m.Req.Header.Set(key, value) 83 return m 84 } 85 86 func (m *mockRequest) SetHeaders(key, value string) *mockRequest { 87 m.Req.Header.Set(key, value) 88 return m 89 } 90 91 func (m *mockRequest) SetPostArg(key, value string) *mockRequest { 92 m.Req.PostArgs().Add(key, value) 93 return m 94 } 95 96 func (m *mockRequest) SetUrlEncodeContentType() *mockRequest { 97 m.Req.Header.SetContentTypeBytes([]byte("application/x-www-form-urlencoded")) 98 return m 99 } 100 101 func (m *mockRequest) SetJSONContentType() *mockRequest { 102 m.Req.Header.SetContentTypeBytes([]byte(consts.MIMEApplicationJSON)) 103 return m 104 } 105 106 func (m *mockRequest) SetProtobufContentType() *mockRequest { 107 m.Req.Header.SetContentTypeBytes([]byte(consts.MIMEPROTOBUF)) 108 return m 109 } 110 111 func (m *mockRequest) SetBody(data []byte) *mockRequest { 112 m.Req.SetBody(data) 113 m.Req.Header.SetContentLength(len(data)) 114 return m 115 } 116 117 func TestBind_BaseType(t *testing.T) { 118 type Req struct { 119 Version int `path:"v"` 120 ID int `query:"id"` 121 Header string `header:"H"` 122 Form string `form:"f"` 123 } 124 125 req := newMockRequest(). 126 SetRequestURI("http://foobar.com?id=12"). 127 SetHeaders("H", "header"). 128 SetPostArg("f", "form"). 129 SetUrlEncodeContentType() 130 var params param.Params 131 params = append(params, param.Param{ 132 Key: "v", 133 Value: "1", 134 }) 135 136 var result Req 137 138 err := DefaultBinder().Bind(req.Req, &result, params) 139 if err != nil { 140 t.Error(err) 141 } 142 assert.DeepEqual(t, 1, result.Version) 143 assert.DeepEqual(t, 12, result.ID) 144 assert.DeepEqual(t, "header", result.Header) 145 assert.DeepEqual(t, "form", result.Form) 146 } 147 148 func TestBind_SliceType(t *testing.T) { 149 type Req struct { 150 ID *[]int `query:"id"` 151 Str [3]string `query:"str"` 152 Byte []byte `query:"b"` 153 } 154 IDs := []int{11, 12, 13} 155 Strs := [3]string{"qwe", "asd", "zxc"} 156 Bytes := []byte("123") 157 158 req := newMockRequest(). 159 SetRequestURI(fmt.Sprintf("http://foobar.com?id=%d&id=%d&id=%d&str=%s&str=%s&str=%s&b=%d&b=%d&b=%d", IDs[0], IDs[1], IDs[2], Strs[0], Strs[1], Strs[2], Bytes[0], Bytes[1], Bytes[2])) 160 161 var result Req 162 163 err := DefaultBinder().Bind(req.Req, &result, nil) 164 if err != nil { 165 t.Error(err) 166 } 167 assert.DeepEqual(t, 3, len(*result.ID)) 168 for idx, val := range IDs { 169 assert.DeepEqual(t, val, (*result.ID)[idx]) 170 } 171 assert.DeepEqual(t, 3, len(result.Str)) 172 for idx, val := range Strs { 173 assert.DeepEqual(t, val, result.Str[idx]) 174 } 175 assert.DeepEqual(t, 3, len(result.Byte)) 176 for idx, val := range Bytes { 177 assert.DeepEqual(t, val, result.Byte[idx]) 178 } 179 } 180 181 func TestBind_StructType(t *testing.T) { 182 type FFF struct { 183 F1 string `query:"F1"` 184 } 185 186 type TTT struct { 187 T1 string `query:"F1"` 188 T2 FFF 189 } 190 191 type Foo struct { 192 F1 string `query:"F1"` 193 F2 string `header:"f2"` 194 F3 TTT 195 } 196 197 type Bar struct { 198 B1 string `query:"B1"` 199 B2 Foo `query:"B2"` 200 } 201 202 var result Bar 203 204 req := newMockRequest().SetRequestURI("http://foobar.com?F1=f1&B1=b1").SetHeader("f2", "f2") 205 206 err := DefaultBinder().Bind(req.Req, &result, nil) 207 if err != nil { 208 t.Error(err) 209 } 210 211 assert.DeepEqual(t, "b1", result.B1) 212 assert.DeepEqual(t, "f1", result.B2.F1) 213 assert.DeepEqual(t, "f2", result.B2.F2) 214 assert.DeepEqual(t, "f1", result.B2.F3.T1) 215 assert.DeepEqual(t, "f1", result.B2.F3.T2.F1) 216 } 217 218 func TestBind_PointerType(t *testing.T) { 219 type TT struct { 220 T1 string `query:"F1"` 221 } 222 223 type Foo struct { 224 F1 *TT `query:"F1"` 225 F2 *******************string `query:"F1"` 226 } 227 228 type Bar struct { 229 B1 ***string `query:"B1"` 230 B2 ****Foo `query:"B2"` 231 B3 []*string `query:"B3"` 232 B4 [2]*int `query:"B4"` 233 } 234 235 result := Bar{} 236 237 F1 := "f1" 238 B1 := "b1" 239 B2 := "b2" 240 B3s := []string{"b31", "b32"} 241 B4s := [2]int{0, 1} 242 243 req := newMockRequest().SetRequestURI(fmt.Sprintf("http://foobar.com?F1=%s&B1=%s&B2=%s&B3=%s&B3=%s&B4=%d&B4=%d", F1, B1, B2, B3s[0], B3s[1], B4s[0], B4s[1])). 244 SetHeader("f2", "f2") 245 246 err := DefaultBinder().Bind(req.Req, &result, nil) 247 if err != nil { 248 t.Error(err) 249 } 250 assert.DeepEqual(t, B1, ***result.B1) 251 assert.DeepEqual(t, F1, (*(****result.B2).F1).T1) 252 assert.DeepEqual(t, F1, *******************(****result.B2).F2) 253 assert.DeepEqual(t, len(B3s), len(result.B3)) 254 for idx, val := range B3s { 255 assert.DeepEqual(t, val, *result.B3[idx]) 256 } 257 assert.DeepEqual(t, len(B4s), len(result.B4)) 258 for idx, val := range B4s { 259 assert.DeepEqual(t, val, *result.B4[idx]) 260 } 261 } 262 263 func TestBind_NestedStruct(t *testing.T) { 264 type Foo struct { 265 F1 string `query:"F1"` 266 } 267 268 type Bar struct { 269 Foo 270 Nested struct { 271 N1 string `query:"F1"` 272 } 273 } 274 275 result := Bar{} 276 277 req := newMockRequest().SetRequestURI("http://foobar.com?F1=qwe") 278 err := DefaultBinder().Bind(req.Req, &result, nil) 279 if err != nil { 280 t.Error(err) 281 } 282 assert.DeepEqual(t, "qwe", result.Foo.F1) 283 assert.DeepEqual(t, "qwe", result.Nested.N1) 284 } 285 286 func TestBind_SliceStruct(t *testing.T) { 287 type Foo struct { 288 F1 string `json:"f1"` 289 } 290 291 type Bar struct { 292 B1 []Foo `query:"F1"` 293 } 294 295 result := Bar{} 296 B1s := []string{"1", "2", "3"} 297 298 req := newMockRequest().SetRequestURI(fmt.Sprintf("http://foobar.com?F1={\"f1\":\"%s\"}&F1={\"f1\":\"%s\"}&F1={\"f1\":\"%s\"}", B1s[0], B1s[1], B1s[2])) 299 err := DefaultBinder().Bind(req.Req, &result, nil) 300 if err != nil { 301 t.Error(err) 302 } 303 assert.DeepEqual(t, len(result.B1), len(B1s)) 304 for idx, val := range B1s { 305 assert.DeepEqual(t, B1s[idx], val) 306 } 307 } 308 309 func TestBind_MapType(t *testing.T) { 310 var result map[string]string 311 req := newMockRequest(). 312 SetJSONContentType(). 313 SetBody([]byte(`{"j1":"j1", "j2":"j2"}`)) 314 err := DefaultBinder().Bind(req.Req, &result, nil) 315 if err != nil { 316 t.Fatal(err) 317 } 318 assert.DeepEqual(t, 2, len(result)) 319 assert.DeepEqual(t, "j1", result["j1"]) 320 assert.DeepEqual(t, "j2", result["j2"]) 321 } 322 323 func TestBind_MapFieldType(t *testing.T) { 324 type Foo struct { 325 F1 ***map[string]string `query:"f1" json:"f1"` 326 } 327 328 req := newMockRequest(). 329 SetRequestURI("http://foobar.com?f1={\"f1\":\"f1\"}"). 330 SetJSONContentType(). 331 SetBody([]byte(`{"j1":"j1", "j2":"j2"}`)) 332 result := Foo{} 333 err := DefaultBinder().Bind(req.Req, &result, nil) 334 if err != nil { 335 t.Fatal(err) 336 } 337 assert.DeepEqual(t, 1, len(***result.F1)) 338 assert.DeepEqual(t, "f1", (***result.F1)["f1"]) 339 340 type Foo2 struct { 341 F1 map[string]string `query:"f1" json:"f1"` 342 } 343 result2 := Foo2{} 344 err = DefaultBinder().Bind(req.Req, &result2, nil) 345 if err != nil { 346 t.Fatal(err) 347 } 348 assert.DeepEqual(t, 1, len(result2.F1)) 349 assert.DeepEqual(t, "f1", result2.F1["f1"]) 350 req = newMockRequest(). 351 SetRequestURI("http://foobar.com?f1={\"f1\":\"f1\"") 352 result2 = Foo2{} 353 err = DefaultBinder().Bind(req.Req, &result2, nil) 354 if err == nil { 355 t.Error(err) 356 } 357 } 358 359 func TestBind_UnexportedField(t *testing.T) { 360 var s struct { 361 A int `query:"a"` 362 b int `query:"b"` 363 } 364 req := newMockRequest(). 365 SetRequestURI("http://foobar.com?a=1&b=2") 366 err := DefaultBinder().Bind(req.Req, &s, nil) 367 if err != nil { 368 t.Fatal(err) 369 } 370 assert.DeepEqual(t, 1, s.A) 371 assert.DeepEqual(t, 0, s.b) 372 } 373 374 func TestBind_NoTagField(t *testing.T) { 375 var s struct { 376 A string 377 B string 378 C string 379 } 380 req := newMockRequest(). 381 SetRequestURI("http://foobar.com?B=b1&C=c1"). 382 SetHeader("A", "a2") 383 384 var params param.Params 385 params = append(params, param.Param{ 386 Key: "B", 387 Value: "b2", 388 }) 389 390 err := DefaultBinder().Bind(req.Req, &s, params) 391 if err != nil { 392 t.Fatal(err) 393 } 394 assert.DeepEqual(t, "a2", s.A) 395 assert.DeepEqual(t, "b2", s.B) 396 assert.DeepEqual(t, "c1", s.C) 397 } 398 399 func TestBind_ZeroValueBind(t *testing.T) { 400 var s struct { 401 A int `query:"a"` 402 B float64 `query:"b"` 403 } 404 req := newMockRequest(). 405 SetRequestURI("http://foobar.com?a=&b") 406 407 bindConfig := &BindConfig{} 408 bindConfig.LooseZeroMode = true 409 binder := NewDefaultBinder(bindConfig) 410 err := binder.Bind(req.Req, &s, nil) 411 if err != nil { 412 t.Fatal(err) 413 } 414 assert.DeepEqual(t, 0, s.A) 415 assert.DeepEqual(t, float64(0), s.B) 416 } 417 418 func TestBind_DefaultValueBind(t *testing.T) { 419 var s struct { 420 A int `default:"15"` 421 B float64 `query:"b" default:"17"` 422 C []int `default:"[15]"` 423 D []string `default:"['qwe','asd']"` 424 F [2]string `default:"['qwe','asd','zxc']"` 425 } 426 req := newMockRequest(). 427 SetRequestURI("http://foobar.com") 428 429 err := DefaultBinder().Bind(req.Req, &s, nil) 430 if err != nil { 431 t.Fatal(err) 432 } 433 assert.DeepEqual(t, 15, s.A) 434 assert.DeepEqual(t, float64(17), s.B) 435 assert.DeepEqual(t, 15, s.C[0]) 436 assert.DeepEqual(t, 2, len(s.D)) 437 assert.DeepEqual(t, "qwe", s.D[0]) 438 assert.DeepEqual(t, "asd", s.D[1]) 439 assert.DeepEqual(t, 2, len(s.F)) 440 assert.DeepEqual(t, "qwe", s.F[0]) 441 assert.DeepEqual(t, "asd", s.F[1]) 442 443 var s2 struct { 444 F [2]string `default:"['qwe']"` 445 } 446 err = DefaultBinder().Bind(req.Req, &s2, nil) 447 if err != nil { 448 t.Fatal(err) 449 } 450 assert.DeepEqual(t, 2, len(s2.F)) 451 assert.DeepEqual(t, "qwe", s2.F[0]) 452 assert.DeepEqual(t, "", s2.F[1]) 453 454 var d struct { 455 D [2]string `default:"qwe"` 456 } 457 458 err = DefaultBinder().Bind(req.Req, &d, nil) 459 if err == nil { 460 t.Fatal("expected err") 461 } 462 } 463 464 func TestBind_RequiredBind(t *testing.T) { 465 var s struct { 466 A int `query:"a,required"` 467 } 468 req := newMockRequest(). 469 SetRequestURI("http://foobar.com") 470 err := DefaultBinder().Bind(req.Req, &s, nil) 471 assert.DeepEqual(t, "'a' field is a 'required' parameter, but the request does not have this parameter", err.Error()) 472 473 req = newMockRequest(). 474 SetRequestURI("http://foobar.com"). 475 SetHeader("A", "1") 476 err = DefaultBinder().Bind(req.Req, &s, nil) 477 if err == nil { 478 t.Fatal("expected error") 479 } 480 481 var d struct { 482 A int `query:"a,required" header:"A"` 483 } 484 err = DefaultBinder().Bind(req.Req, &d, nil) 485 if err != nil { 486 t.Fatal(err) 487 } 488 assert.DeepEqual(t, 1, d.A) 489 } 490 491 func TestBind_TypedefType(t *testing.T) { 492 type Foo string 493 type Bar *int 494 type T struct { 495 T1 string `query:"a"` 496 } 497 type TT T 498 499 var s struct { 500 A Foo `query:"a"` 501 B Bar `query:"b"` 502 T1 TT 503 } 504 req := newMockRequest(). 505 SetRequestURI("http://foobar.com?a=1&b=2") 506 err := DefaultBinder().Bind(req.Req, &s, nil) 507 if err != nil { 508 t.Fatal(err) 509 } 510 assert.DeepEqual(t, Foo("1"), s.A) 511 assert.DeepEqual(t, 2, *s.B) 512 assert.DeepEqual(t, "1", s.T1.T1) 513 } 514 515 type EnumType int64 516 517 const ( 518 EnumType_TWEET EnumType = 0 519 EnumType_RETWEET EnumType = 2 520 ) 521 522 func (p EnumType) String() string { 523 switch p { 524 case EnumType_TWEET: 525 return "TWEET" 526 case EnumType_RETWEET: 527 return "RETWEET" 528 } 529 return "<UNSET>" 530 } 531 532 func TestBind_EnumBind(t *testing.T) { 533 var s struct { 534 A EnumType `query:"a"` 535 B EnumType `query:"b"` 536 } 537 req := newMockRequest(). 538 SetRequestURI("http://foobar.com?a=0&b=2") 539 err := DefaultBinder().Bind(req.Req, &s, nil) 540 if err != nil { 541 t.Fatal(err) 542 } 543 } 544 545 type CustomizedDecode struct { 546 A string 547 } 548 549 func TestBind_CustomizedTypeDecode(t *testing.T) { 550 type Foo struct { 551 F ***CustomizedDecode `query:"a"` 552 } 553 554 bindConfig := &BindConfig{} 555 err := bindConfig.RegTypeUnmarshal(reflect.TypeOf(CustomizedDecode{}), func(req *protocol.Request, params param.Params, text string) (reflect.Value, error) { 556 q1 := req.URI().QueryArgs().Peek("a") 557 if len(q1) == 0 { 558 return reflect.Value{}, fmt.Errorf("can be nil") 559 } 560 val := CustomizedDecode{ 561 A: string(q1), 562 } 563 return reflect.ValueOf(val), nil 564 }) 565 if err != nil { 566 t.Fatal(err) 567 } 568 binder := NewDefaultBinder(bindConfig) 569 570 req := newMockRequest(). 571 SetRequestURI("http://foobar.com?a=1&b=2") 572 result := Foo{} 573 err = binder.Bind(req.Req, &result, nil) 574 if err != nil { 575 t.Fatal(err) 576 } 577 assert.DeepEqual(t, "1", (***result.F).A) 578 579 type Bar struct { 580 B *Foo 581 } 582 583 result2 := Bar{} 584 err = binder.Bind(req.Req, &result2, nil) 585 if err != nil { 586 t.Error(err) 587 } 588 assert.DeepEqual(t, "1", (***(*result2.B).F).A) 589 } 590 591 func TestBind_CustomizedTypeDecodeForPanic(t *testing.T) { 592 defer func() { 593 if r := recover(); r == nil { 594 t.Errorf("expect a panic, but get nil") 595 } 596 }() 597 598 bindConfig := &BindConfig{} 599 bindConfig.MustRegTypeUnmarshal(reflect.TypeOf(string("")), func(req *protocol.Request, params param.Params, text string) (reflect.Value, error) { 600 return reflect.Value{}, nil 601 }) 602 } 603 604 func TestBind_JSON(t *testing.T) { 605 type Req struct { 606 J1 string `json:"j1"` 607 J2 int `json:"j2" query:"j2"` // 1. json unmarshal 2. query binding cover 608 J3 []byte `json:"j3"` 609 J4 [2]string `json:"j4"` 610 } 611 J3s := []byte("12") 612 J4s := [2]string{"qwe", "asd"} 613 614 req := newMockRequest(). 615 SetRequestURI("http://foobar.com?j2=13"). 616 SetJSONContentType(). 617 SetBody([]byte(fmt.Sprintf(`{"j1":"j1", "j2":12, "j3":[%d, %d], "j4":["%s", "%s"]}`, J3s[0], J3s[1], J4s[0], J4s[1]))) 618 var result Req 619 err := DefaultBinder().Bind(req.Req, &result, nil) 620 if err != nil { 621 t.Error(err) 622 } 623 assert.DeepEqual(t, "j1", result.J1) 624 assert.DeepEqual(t, 13, result.J2) 625 for idx, val := range J3s { 626 assert.DeepEqual(t, val, result.J3[idx]) 627 } 628 for idx, val := range J4s { 629 assert.DeepEqual(t, val, result.J4[idx]) 630 } 631 } 632 633 func TestBind_ResetJSONUnmarshal(t *testing.T) { 634 bindConfig := &BindConfig{} 635 bindConfig.UseStdJSONUnmarshaler() 636 binder := NewDefaultBinder(bindConfig) 637 type Req struct { 638 J1 string `json:"j1"` 639 J2 int `json:"j2"` 640 J3 []byte `json:"j3"` 641 J4 [2]string `json:"j4"` 642 } 643 J3s := []byte("12") 644 J4s := [2]string{"qwe", "asd"} 645 646 req := newMockRequest(). 647 SetJSONContentType(). 648 SetBody([]byte(fmt.Sprintf(`{"j1":"j1", "j2":12, "j3":[%d, %d], "j4":["%s", "%s"]}`, J3s[0], J3s[1], J4s[0], J4s[1]))) 649 var result Req 650 err := binder.Bind(req.Req, &result, nil) 651 if err != nil { 652 t.Error(err) 653 } 654 assert.DeepEqual(t, "j1", result.J1) 655 assert.DeepEqual(t, 12, result.J2) 656 for idx, val := range J3s { 657 assert.DeepEqual(t, val, result.J3[idx]) 658 } 659 for idx, val := range J4s { 660 assert.DeepEqual(t, val, result.J4[idx]) 661 } 662 } 663 664 func TestBind_FileBind(t *testing.T) { 665 type Nest struct { 666 N multipart.FileHeader `file_name:"d"` 667 } 668 669 var s struct { 670 A *multipart.FileHeader `file_name:"a"` 671 B *multipart.FileHeader `form:"b"` 672 C multipart.FileHeader 673 D **Nest `file_name:"d"` 674 } 675 fileName := "binder_test.go" 676 req := newMockRequest(). 677 SetRequestURI("http://foobar.com"). 678 SetFile("a", fileName). 679 SetFile("b", fileName). 680 SetFile("C", fileName). 681 SetFile("d", fileName) 682 // to parse multipart files 683 req2 := req2.GetHTTP1Request(req.Req) 684 _ = req2.String() 685 err := DefaultBinder().Bind(req.Req, &s, nil) 686 if err != nil { 687 t.Fatal(err) 688 } 689 assert.DeepEqual(t, fileName, s.A.Filename) 690 assert.DeepEqual(t, fileName, s.B.Filename) 691 assert.DeepEqual(t, fileName, s.C.Filename) 692 assert.DeepEqual(t, fileName, (**s.D).N.Filename) 693 } 694 695 func TestBind_FileBindWithNoFile(t *testing.T) { 696 var s struct { 697 A *multipart.FileHeader `file_name:"a"` 698 B *multipart.FileHeader `form:"b"` 699 C *multipart.FileHeader 700 } 701 fileName := "binder_test.go" 702 req := newMockRequest(). 703 SetRequestURI("http://foobar.com"). 704 SetFile("a", fileName). 705 SetFile("b", fileName) 706 // to parse multipart files 707 req2 := req2.GetHTTP1Request(req.Req) 708 _ = req2.String() 709 err := DefaultBinder().Bind(req.Req, &s, nil) 710 if err != nil { 711 t.Fatalf("unexpected err: %v", err) 712 } 713 assert.DeepEqual(t, fileName, s.A.Filename) 714 assert.DeepEqual(t, fileName, s.B.Filename) 715 if s.C != nil { 716 t.Fatalf("expected a nil for s.C") 717 } 718 } 719 720 func TestBind_FileSliceBind(t *testing.T) { 721 type Nest struct { 722 N *[]*multipart.FileHeader `form:"b"` 723 } 724 var s struct { 725 A []multipart.FileHeader `form:"a"` 726 B [3]multipart.FileHeader `form:"b"` 727 C []*multipart.FileHeader `form:"b"` 728 D Nest 729 } 730 fileName := "binder_test.go" 731 req := newMockRequest(). 732 SetRequestURI("http://foobar.com"). 733 SetFile("a", fileName). 734 SetFile("a", fileName). 735 SetFile("a", fileName). 736 SetFile("b", fileName). 737 SetFile("b", fileName). 738 SetFile("b", fileName) 739 // to parse multipart files 740 req2 := req2.GetHTTP1Request(req.Req) 741 _ = req2.String() 742 err := DefaultBinder().Bind(req.Req, &s, nil) 743 if err != nil { 744 t.Fatal(err) 745 } 746 assert.DeepEqual(t, 3, len(s.A)) 747 for _, file := range s.A { 748 assert.DeepEqual(t, fileName, file.Filename) 749 } 750 assert.DeepEqual(t, 3, len(s.B)) 751 for _, file := range s.B { 752 assert.DeepEqual(t, fileName, file.Filename) 753 } 754 assert.DeepEqual(t, 3, len(s.C)) 755 for _, file := range s.C { 756 assert.DeepEqual(t, fileName, file.Filename) 757 } 758 assert.DeepEqual(t, 3, len(*s.D.N)) 759 for _, file := range *s.D.N { 760 assert.DeepEqual(t, fileName, file.Filename) 761 } 762 } 763 764 func TestBind_AnonymousField(t *testing.T) { 765 type nest struct { 766 n1 string `query:"n1"` // bind default value 767 N2 ***string `query:"n2"` // bind n2 value 768 string `query:"n3"` // bind default value 769 } 770 771 var s struct { 772 s1 int `query:"s1"` // bind default value 773 int `query:"s2"` // bind default value 774 nest 775 } 776 req := newMockRequest(). 777 SetRequestURI("http://foobar.com?s1=1&s2=2&n1=1&n2=2&n3=3") 778 err := DefaultBinder().Bind(req.Req, &s, nil) 779 if err != nil { 780 t.Fatal(err) 781 } 782 assert.DeepEqual(t, 0, s.s1) 783 assert.DeepEqual(t, 0, s.int) 784 assert.DeepEqual(t, "", s.nest.n1) 785 assert.DeepEqual(t, "2", ***s.nest.N2) 786 assert.DeepEqual(t, "", s.nest.string) 787 } 788 789 func TestBind_IgnoreField(t *testing.T) { 790 type Req struct { 791 Version int `path:"-"` 792 ID int `query:"-"` 793 Header string `header:"-"` 794 Form string `form:"-"` 795 } 796 797 req := newMockRequest(). 798 SetRequestURI("http://foobar.com?ID=12"). 799 SetHeaders("Header", "header"). 800 SetPostArg("Form", "form"). 801 SetUrlEncodeContentType() 802 var params param.Params 803 params = append(params, param.Param{ 804 Key: "Version", 805 Value: "1", 806 }) 807 808 var result Req 809 810 err := DefaultBinder().Bind(req.Req, &result, params) 811 if err != nil { 812 t.Error(err) 813 } 814 assert.DeepEqual(t, 0, result.Version) 815 assert.DeepEqual(t, 0, result.ID) 816 assert.DeepEqual(t, "", result.Header) 817 assert.DeepEqual(t, "", result.Form) 818 } 819 820 func TestBind_DefaultTag(t *testing.T) { 821 type Req struct { 822 Version int 823 ID int 824 Header string 825 Form string 826 } 827 type Req2 struct { 828 Version int 829 ID int 830 Header string 831 Form string 832 } 833 req := newMockRequest(). 834 SetRequestURI("http://foobar.com?ID=12"). 835 SetHeaders("Header", "header"). 836 SetPostArg("Form", "form"). 837 SetUrlEncodeContentType() 838 var params param.Params 839 params = append(params, param.Param{ 840 Key: "Version", 841 Value: "1", 842 }) 843 var result Req 844 err := DefaultBinder().Bind(req.Req, &result, params) 845 if err != nil { 846 t.Error(err) 847 } 848 assert.DeepEqual(t, 1, result.Version) 849 assert.DeepEqual(t, 12, result.ID) 850 assert.DeepEqual(t, "header", result.Header) 851 assert.DeepEqual(t, "form", result.Form) 852 853 bindConfig := &BindConfig{} 854 bindConfig.DisableDefaultTag = true 855 binder := NewDefaultBinder(bindConfig) 856 result2 := Req2{} 857 err = binder.Bind(req.Req, &result2, params) 858 if err != nil { 859 t.Error(err) 860 } 861 assert.DeepEqual(t, 0, result2.Version) 862 assert.DeepEqual(t, 0, result2.ID) 863 assert.DeepEqual(t, "", result2.Header) 864 assert.DeepEqual(t, "", result2.Form) 865 } 866 867 func TestBind_StructFieldResolve(t *testing.T) { 868 type Nested struct { 869 A int `query:"a" json:"a"` 870 B int `query:"b" json:"b"` 871 } 872 type Req struct { 873 N Nested `query:"n"` 874 } 875 876 req := newMockRequest(). 877 SetRequestURI("http://foobar.com?n={\"a\":1,\"b\":2}"). 878 SetHeaders("Header", "header"). 879 SetPostArg("Form", "form"). 880 SetUrlEncodeContentType() 881 var result Req 882 bindConfig := &BindConfig{} 883 bindConfig.DisableStructFieldResolve = false 884 binder := NewDefaultBinder(bindConfig) 885 err := binder.Bind(req.Req, &result, nil) 886 if err != nil { 887 t.Error(err) 888 } 889 assert.DeepEqual(t, 1, result.N.A) 890 assert.DeepEqual(t, 2, result.N.B) 891 892 req = newMockRequest(). 893 SetRequestURI("http://foobar.com?n={\"a\":1,\"b\":2}&a=11&b=22"). 894 SetHeaders("Header", "header"). 895 SetPostArg("Form", "form"). 896 SetUrlEncodeContentType() 897 err = DefaultBinder().Bind(req.Req, &result, nil) 898 if err != nil { 899 t.Error(err) 900 } 901 assert.DeepEqual(t, 11, result.N.A) 902 assert.DeepEqual(t, 22, result.N.B) 903 } 904 905 func TestBind_JSONRequiredField(t *testing.T) { 906 type Nested2 struct { 907 C int `json:"c,required"` 908 D int `json:"dd,required"` 909 } 910 type Nested struct { 911 A int `json:"a,required"` 912 B int `json:"b,required"` 913 N2 Nested2 `json:"n2"` 914 } 915 type Req struct { 916 N Nested `json:"n,required"` 917 } 918 bodyBytes := []byte(`{ 919 "n": { 920 "a": 1, 921 "b": 2, 922 "n2": { 923 "dd": 4 924 } 925 } 926 }`) 927 req := newMockRequest(). 928 SetRequestURI("http://foobar.com?j2=13"). 929 SetJSONContentType(). 930 SetBody(bodyBytes) 931 var result Req 932 err := DefaultBinder().Bind(req.Req, &result, nil) 933 if err == nil { 934 t.Errorf("expected an error, but get nil") 935 } 936 assert.DeepEqual(t, "'c' field is a 'required' parameter, but the request body does not have this parameter 'n.n2.c'", err.Error()) 937 assert.DeepEqual(t, 1, result.N.A) 938 assert.DeepEqual(t, 2, result.N.B) 939 assert.DeepEqual(t, 0, result.N.N2.C) 940 assert.DeepEqual(t, 4, result.N.N2.D) 941 942 bodyBytes = []byte(`{ 943 "n": { 944 "a": 1, 945 "b": 2 946 } 947 }`) 948 req = newMockRequest(). 949 SetRequestURI("http://foobar.com?j2=13"). 950 SetJSONContentType(). 951 SetBody(bodyBytes) 952 var result2 Req 953 err = DefaultBinder().Bind(req.Req, &result2, nil) 954 if err != nil { 955 t.Error(err) 956 } 957 assert.DeepEqual(t, 1, result2.N.A) 958 assert.DeepEqual(t, 2, result2.N.B) 959 assert.DeepEqual(t, 0, result2.N.N2.C) 960 assert.DeepEqual(t, 0, result2.N.N2.D) 961 } 962 963 func TestValidate_MultipleValidate(t *testing.T) { 964 type Test1 struct { 965 A int `query:"a" vd:"$>10"` 966 } 967 req := newMockRequest(). 968 SetRequestURI("http://foobar.com?a=9") 969 var result Test1 970 err := DefaultBinder().BindAndValidate(req.Req, &result, nil) 971 if err == nil { 972 t.Fatalf("expected an error, but get nil") 973 } 974 } 975 976 func TestBind_BindQuery(t *testing.T) { 977 type Req struct { 978 Q1 int `query:"q1"` 979 Q2 int 980 Q3 string 981 Q4 string 982 Q5 []int 983 } 984 985 req := newMockRequest(). 986 SetRequestURI("http://foobar.com?q1=1&Q2=2&Q3=3&Q4=4&Q5=51&Q5=52") 987 988 var result Req 989 990 err := DefaultBinder().BindQuery(req.Req, &result) 991 if err != nil { 992 t.Error(err) 993 } 994 assert.DeepEqual(t, 1, result.Q1) 995 assert.DeepEqual(t, 2, result.Q2) 996 assert.DeepEqual(t, "3", result.Q3) 997 assert.DeepEqual(t, "4", result.Q4) 998 assert.DeepEqual(t, 51, result.Q5[0]) 999 assert.DeepEqual(t, 52, result.Q5[1]) 1000 } 1001 1002 func TestBind_LooseMode(t *testing.T) { 1003 bindConfig := &BindConfig{} 1004 bindConfig.LooseZeroMode = false 1005 binder := NewDefaultBinder(bindConfig) 1006 type Req struct { 1007 ID int `query:"id"` 1008 } 1009 1010 req := newMockRequest(). 1011 SetRequestURI("http://foobar.com?id=") 1012 1013 var result Req 1014 1015 err := binder.Bind(req.Req, &result, nil) 1016 if err == nil { 1017 t.Fatal("expected err") 1018 } 1019 assert.DeepEqual(t, 0, result.ID) 1020 1021 bindConfig.LooseZeroMode = true 1022 binder = NewDefaultBinder(bindConfig) 1023 var result2 Req 1024 1025 err = binder.Bind(req.Req, &result2, nil) 1026 if err != nil { 1027 t.Error(err) 1028 } 1029 assert.DeepEqual(t, 0, result.ID) 1030 } 1031 1032 func TestBind_NonStruct(t *testing.T) { 1033 req := newMockRequest(). 1034 SetRequestURI("http://foobar.com?id=1&id=2") 1035 var id interface{} 1036 err := DefaultBinder().Bind(req.Req, &id, nil) 1037 if err != nil { 1038 t.Error(err) 1039 } 1040 1041 err = DefaultBinder().BindAndValidate(req.Req, &id, nil) 1042 if err != nil { 1043 t.Error(err) 1044 } 1045 } 1046 1047 func TestBind_BindTag(t *testing.T) { 1048 type Req struct { 1049 Query string 1050 Header string 1051 Path string 1052 Form string 1053 } 1054 req := newMockRequest(). 1055 SetRequestURI("http://foobar.com?Query=query"). 1056 SetHeader("Header", "header"). 1057 SetPostArg("Form", "form") 1058 var params param.Params 1059 params = append(params, param.Param{ 1060 Key: "Path", 1061 Value: "path", 1062 }) 1063 result := Req{} 1064 1065 // test query tag 1066 err := DefaultBinder().BindQuery(req.Req, &result) 1067 if err != nil { 1068 t.Error(err) 1069 } 1070 assert.DeepEqual(t, "query", result.Query) 1071 1072 // test header tag 1073 result = Req{} 1074 err = DefaultBinder().BindHeader(req.Req, &result) 1075 if err != nil { 1076 t.Error(err) 1077 } 1078 assert.DeepEqual(t, "header", result.Header) 1079 1080 // test form tag 1081 result = Req{} 1082 err = DefaultBinder().BindForm(req.Req, &result) 1083 if err != nil { 1084 t.Error(err) 1085 } 1086 assert.DeepEqual(t, "form", result.Form) 1087 1088 // test path tag 1089 result = Req{} 1090 err = DefaultBinder().BindPath(req.Req, &result, params) 1091 if err != nil { 1092 t.Error(err) 1093 } 1094 assert.DeepEqual(t, "path", result.Path) 1095 1096 // test json tag 1097 req = newMockRequest(). 1098 SetRequestURI("http://foobar.com"). 1099 SetJSONContentType(). 1100 SetBody([]byte("{\n \"Query\": \"query\",\n \"Path\": \"path\",\n \"Header\": \"header\",\n \"Form\": \"form\"\n}")) 1101 result = Req{} 1102 err = DefaultBinder().BindJSON(req.Req, &result) 1103 if err != nil { 1104 t.Error(err) 1105 } 1106 assert.DeepEqual(t, "form", result.Form) 1107 assert.DeepEqual(t, "query", result.Query) 1108 assert.DeepEqual(t, "header", result.Header) 1109 assert.DeepEqual(t, "path", result.Path) 1110 } 1111 1112 func TestBind_BindAndValidate(t *testing.T) { 1113 type Req struct { 1114 ID int `query:"id" vd:"$>10"` 1115 } 1116 req := newMockRequest(). 1117 SetRequestURI("http://foobar.com?id=12") 1118 1119 // test bindAndValidate 1120 var result Req 1121 err := BindAndValidate(req.Req, &result, nil) 1122 if err != nil { 1123 t.Error(err) 1124 } 1125 assert.DeepEqual(t, 12, result.ID) 1126 1127 // test bind 1128 result = Req{} 1129 err = Bind(req.Req, &result, nil) 1130 if err != nil { 1131 t.Error(err) 1132 } 1133 assert.DeepEqual(t, 12, result.ID) 1134 1135 // test validate 1136 req = newMockRequest(). 1137 SetRequestURI("http://foobar.com?id=9") 1138 result = Req{} 1139 err = Bind(req.Req, &result, nil) 1140 if err != nil { 1141 t.Error(err) 1142 } 1143 err = Validate(result) 1144 if err == nil { 1145 t.Errorf("expect an error, but get nil") 1146 } 1147 assert.DeepEqual(t, 9, result.ID) 1148 } 1149 1150 func TestBind_FastPath(t *testing.T) { 1151 type Req struct { 1152 ID int `query:"id" vd:"$>10"` 1153 } 1154 req := newMockRequest(). 1155 SetRequestURI("http://foobar.com?id=12") 1156 1157 // test bindAndValidate 1158 var result Req 1159 err := BindAndValidate(req.Req, &result, nil) 1160 if err != nil { 1161 t.Error(err) 1162 } 1163 assert.DeepEqual(t, 12, result.ID) 1164 // execute multiple times, test cache 1165 for i := 0; i < 10; i++ { 1166 result = Req{} 1167 err := BindAndValidate(req.Req, &result, nil) 1168 if err != nil { 1169 t.Error(err) 1170 } 1171 assert.DeepEqual(t, 12, result.ID) 1172 } 1173 } 1174 1175 func TestBind_NonPointer(t *testing.T) { 1176 type Req struct { 1177 ID int `query:"id" vd:"$>10"` 1178 } 1179 req := newMockRequest(). 1180 SetRequestURI("http://foobar.com?id=12") 1181 1182 // test bindAndValidate 1183 var result Req 1184 err := BindAndValidate(req.Req, result, nil) 1185 if err == nil { 1186 t.Error("expect an error, but get nil") 1187 } 1188 1189 err = Bind(req.Req, result, nil) 1190 if err == nil { 1191 t.Error("expect an error, but get nil") 1192 } 1193 } 1194 1195 func TestBind_PreBind(t *testing.T) { 1196 type Req struct { 1197 Query string 1198 Header string 1199 Path string 1200 Form string 1201 } 1202 // test json tag 1203 req := newMockRequest(). 1204 SetRequestURI("http://foobar.com"). 1205 SetJSONContentType(). 1206 SetBody([]byte("\n \"Query\": \"query\",\n \"Path\": \"path\",\n \"Header\": \"header\",\n \"Form\": \"form\"\n}")) 1207 result := Req{} 1208 err := DefaultBinder().Bind(req.Req, &result, nil) 1209 if err == nil { 1210 t.Error("expect an error, but get nil") 1211 } 1212 err = DefaultBinder().BindAndValidate(req.Req, &result, nil) 1213 if err == nil { 1214 t.Error("expect an error, but get nil") 1215 } 1216 } 1217 1218 func TestBind_BindProtobuf(t *testing.T) { 1219 data := testdata.HertzReq{Name: "hertz"} 1220 body, err := proto.Marshal(&data) 1221 if err != nil { 1222 t.Fatal(err) 1223 } 1224 req := newMockRequest(). 1225 SetRequestURI("http://foobar.com"). 1226 SetProtobufContentType(). 1227 SetBody(body) 1228 1229 result := testdata.HertzReq{} 1230 err = DefaultBinder().BindAndValidate(req.Req, &result, nil) 1231 if err != nil { 1232 t.Error(err) 1233 } 1234 assert.DeepEqual(t, "hertz", result.Name) 1235 1236 result = testdata.HertzReq{} 1237 err = DefaultBinder().BindProtobuf(req.Req, &result) 1238 if err != nil { 1239 t.Error(err) 1240 } 1241 assert.DeepEqual(t, "hertz", result.Name) 1242 } 1243 1244 func TestBind_PointerStruct(t *testing.T) { 1245 bindConfig := &BindConfig{} 1246 bindConfig.DisableStructFieldResolve = false 1247 binder := NewDefaultBinder(bindConfig) 1248 type Foo struct { 1249 F1 string `query:"F1"` 1250 } 1251 type Bar struct { 1252 B1 **Foo `query:"B1,required"` 1253 } 1254 query := make(url.Values) 1255 query.Add("B1", "{\n \"F1\": \"111\"\n}") 1256 1257 var result Bar 1258 req := newMockRequest(). 1259 SetRequestURI(fmt.Sprintf("http://foobar.com?%s", query.Encode())) 1260 1261 err := binder.Bind(req.Req, &result, nil) 1262 if err != nil { 1263 t.Error(err) 1264 } 1265 assert.DeepEqual(t, "111", (**result.B1).F1) 1266 1267 result = Bar{} 1268 req = newMockRequest(). 1269 SetRequestURI(fmt.Sprintf("http://foobar.com?%s&F1=222", query.Encode())) 1270 err = binder.Bind(req.Req, &result, nil) 1271 if err != nil { 1272 t.Error(err) 1273 } 1274 assert.DeepEqual(t, "222", (**result.B1).F1) 1275 } 1276 1277 func TestBind_StructRequired(t *testing.T) { 1278 bindConfig := &BindConfig{} 1279 bindConfig.DisableStructFieldResolve = false 1280 binder := NewDefaultBinder(bindConfig) 1281 type Foo struct { 1282 F1 string `query:"F1"` 1283 } 1284 type Bar struct { 1285 B1 **Foo `query:"B1,required"` 1286 } 1287 1288 var result Bar 1289 req := newMockRequest(). 1290 SetRequestURI("http://foobar.com") 1291 1292 err := binder.Bind(req.Req, &result, nil) 1293 if err == nil { 1294 t.Error("expect an error, but get nil") 1295 } 1296 1297 type Bar2 struct { 1298 B1 **Foo `query:"B1"` 1299 } 1300 var result2 Bar2 1301 req = newMockRequest(). 1302 SetRequestURI("http://foobar.com") 1303 1304 err = binder.Bind(req.Req, &result2, nil) 1305 if err != nil { 1306 t.Error(err) 1307 } 1308 } 1309 1310 func TestBind_StructErrorToWarn(t *testing.T) { 1311 bindConfig := &BindConfig{} 1312 bindConfig.DisableStructFieldResolve = false 1313 binder := NewDefaultBinder(bindConfig) 1314 type Foo struct { 1315 F1 string `query:"F1"` 1316 } 1317 type Bar struct { 1318 B1 **Foo `query:"B1,required"` 1319 } 1320 1321 var result Bar 1322 req := newMockRequest(). 1323 SetRequestURI("http://foobar.com?B1=111&F1=222") 1324 1325 err := binder.Bind(req.Req, &result, nil) 1326 // transfer 'unmarsahl err' to 'warn' 1327 if err != nil { 1328 t.Error(err) 1329 } 1330 assert.DeepEqual(t, "222", (**result.B1).F1) 1331 1332 type Bar2 struct { 1333 B1 Foo `query:"B1,required"` 1334 } 1335 var result2 Bar2 1336 err = binder.Bind(req.Req, &result2, nil) 1337 // transfer 'unmarsahl err' to 'warn' 1338 if err != nil { 1339 t.Error(err) 1340 } 1341 assert.DeepEqual(t, "222", result2.B1.F1) 1342 } 1343 1344 func TestBind_DisallowUnknownFieldsConfig(t *testing.T) { 1345 bindConfig := &BindConfig{} 1346 bindConfig.EnableDecoderDisallowUnknownFields = true 1347 binder := NewDefaultBinder(bindConfig) 1348 type FooStructUseNumber struct { 1349 Foo interface{} `json:"foo"` 1350 } 1351 req := newMockRequest(). 1352 SetRequestURI("http://foobar.com"). 1353 SetJSONContentType(). 1354 SetBody([]byte(`{"foo": 123,"bar": "456"}`)) 1355 var result FooStructUseNumber 1356 1357 err := binder.BindJSON(req.Req, &result) 1358 if err == nil { 1359 t.Errorf("expected an error, but get nil") 1360 } 1361 } 1362 1363 func TestBind_UseNumberConfig(t *testing.T) { 1364 bindConfig := &BindConfig{} 1365 bindConfig.EnableDecoderUseNumber = true 1366 binder := NewDefaultBinder(bindConfig) 1367 type FooStructUseNumber struct { 1368 Foo interface{} `json:"foo"` 1369 } 1370 req := newMockRequest(). 1371 SetRequestURI("http://foobar.com"). 1372 SetJSONContentType(). 1373 SetBody([]byte(`{"foo": 123}`)) 1374 var result FooStructUseNumber 1375 1376 err := binder.BindJSON(req.Req, &result) 1377 if err != nil { 1378 t.Error(err) 1379 } 1380 v, err := result.Foo.(json.Number).Int64() 1381 if err != nil { 1382 t.Error(err) 1383 } 1384 assert.DeepEqual(t, int64(123), v) 1385 } 1386 1387 func TestBind_InterfaceType(t *testing.T) { 1388 type Bar struct { 1389 B1 interface{} `query:"B1"` 1390 } 1391 1392 var result Bar 1393 query := make(url.Values) 1394 query.Add("B1", `{"B1":"111"}`) 1395 req := newMockRequest(). 1396 SetRequestURI(fmt.Sprintf("http://foobar.com?%s", query.Encode())) 1397 err := DefaultBinder().Bind(req.Req, &result, nil) 1398 if err != nil { 1399 t.Error(err) 1400 } 1401 1402 type Bar2 struct { 1403 B2 *interface{} `query:"B1"` 1404 } 1405 1406 var result2 Bar2 1407 err = DefaultBinder().Bind(req.Req, &result2, nil) 1408 if err != nil { 1409 t.Error(err) 1410 } 1411 } 1412 1413 func Test_BindHeaderNormalize(t *testing.T) { 1414 type Req struct { 1415 Header string `header:"h"` 1416 } 1417 1418 req := newMockRequest(). 1419 SetRequestURI("http://foobar.com"). 1420 SetHeaders("h", "header") 1421 var result Req 1422 1423 err := DefaultBinder().Bind(req.Req, &result, nil) 1424 if err != nil { 1425 t.Error(err) 1426 } 1427 assert.DeepEqual(t, "header", result.Header) 1428 req = newMockRequest(). 1429 SetRequestURI("http://foobar.com"). 1430 SetHeaders("H", "header") 1431 err = DefaultBinder().Bind(req.Req, &result, nil) 1432 if err != nil { 1433 t.Error(err) 1434 } 1435 assert.DeepEqual(t, "header", result.Header) 1436 1437 type Req2 struct { 1438 Header string `header:"H"` 1439 } 1440 1441 req2 := newMockRequest(). 1442 SetRequestURI("http://foobar.com"). 1443 SetHeaders("h", "header") 1444 var result2 Req2 1445 1446 err2 := DefaultBinder().Bind(req2.Req, &result2, nil) 1447 if err != nil { 1448 t.Error(err2) 1449 } 1450 assert.DeepEqual(t, "header", result2.Header) 1451 req2 = newMockRequest(). 1452 SetRequestURI("http://foobar.com"). 1453 SetHeaders("H", "header") 1454 err2 = DefaultBinder().Bind(req2.Req, &result2, nil) 1455 if err2 != nil { 1456 t.Error(err2) 1457 } 1458 assert.DeepEqual(t, "header", result2.Header) 1459 1460 type Req3 struct { 1461 Header string `header:"h"` 1462 } 1463 1464 // without normalize, the header key & tag key need to be consistent 1465 req3 := newMockRequest(). 1466 SetRequestURI("http://foobar.com") 1467 req3.Req.Header.DisableNormalizing() 1468 req3.SetHeaders("h", "header") 1469 var result3 Req3 1470 err3 := DefaultBinder().Bind(req3.Req, &result3, nil) 1471 if err3 != nil { 1472 t.Error(err3) 1473 } 1474 assert.DeepEqual(t, "header", result3.Header) 1475 req3 = newMockRequest(). 1476 SetRequestURI("http://foobar.com") 1477 req3.Req.Header.DisableNormalizing() 1478 req3.SetHeaders("H", "header") 1479 result3 = Req3{} 1480 err3 = DefaultBinder().Bind(req3.Req, &result3, nil) 1481 if err3 != nil { 1482 t.Error(err3) 1483 } 1484 assert.DeepEqual(t, "", result3.Header) 1485 } 1486 1487 type ValidateError struct { 1488 ErrType, FailField, Msg string 1489 } 1490 1491 // Error implements error interface. 1492 func (e *ValidateError) Error() string { 1493 if e.Msg != "" { 1494 return e.ErrType + ": expr_path=" + e.FailField + ", cause=" + e.Msg 1495 } 1496 return e.ErrType + ": expr_path=" + e.FailField + ", cause=invalid" 1497 } 1498 1499 func Test_ValidatorErrorFactory(t *testing.T) { 1500 type TestBind struct { 1501 A string `query:"a,required"` 1502 } 1503 1504 r := protocol.NewRequest("GET", "/foo", nil) 1505 r.SetRequestURI("/foo/bar?b=20") 1506 CustomValidateErrFunc := func(failField, msg string) error { 1507 err := ValidateError{ 1508 ErrType: "validateErr", 1509 FailField: "[validateFailField]: " + failField, 1510 Msg: "[validateErrMsg]: " + msg, 1511 } 1512 1513 return &err 1514 } 1515 1516 validateConfig := NewValidateConfig() 1517 validateConfig.SetValidatorErrorFactory(CustomValidateErrFunc) 1518 validator := NewValidator(validateConfig) 1519 1520 var req TestBind 1521 err := Bind(r, &req, nil) 1522 if err == nil { 1523 t.Fatalf("unexpected nil, expected an error") 1524 } 1525 assert.DeepEqual(t, "'a' field is a 'required' parameter, but the request does not have this parameter", err.Error()) 1526 1527 type TestValidate struct { 1528 B int `query:"b" vd:"$>100"` 1529 } 1530 1531 var reqValidate TestValidate 1532 err = Bind(r, &reqValidate, nil) 1533 if err != nil { 1534 t.Fatalf("unexpected error: %v", err) 1535 } 1536 err = validator.ValidateStruct(&reqValidate) 1537 if err == nil { 1538 t.Fatalf("unexpected nil, expected an error") 1539 } 1540 assert.DeepEqual(t, "validateErr: expr_path=[validateFailField]: B, cause=[validateErrMsg]: ", err.Error()) 1541 } 1542 1543 // Test_Issue964 used to the cover issue for time.Time 1544 func Test_Issue964(t *testing.T) { 1545 type CreateReq struct { 1546 StartAt *time.Time `json:"startAt"` 1547 } 1548 r := newMockRequest().SetBody([]byte("{\n \"startAt\": \"2006-01-02T15:04:05+07:00\"\n}")).SetJSONContentType() 1549 var req CreateReq 1550 err := DefaultBinder().BindAndValidate(r.Req, &req, nil) 1551 if err != nil { 1552 t.Error(err) 1553 } 1554 assert.DeepEqual(t, "2006-01-02 15:04:05 +0700 +0700", req.StartAt.String()) 1555 r = newMockRequest() 1556 req = CreateReq{} 1557 err = DefaultBinder().BindAndValidate(r.Req, &req, nil) 1558 if err != nil { 1559 t.Error(err) 1560 } 1561 if req.StartAt != nil { 1562 t.Error("expected nil") 1563 } 1564 } 1565 1566 type reqSameType struct { 1567 Parent *reqSameType `json:"parent"` 1568 Children []reqSameType `json:"children"` 1569 Foo1 reqSameType2 `json:"foo1"` 1570 A string `json:"a"` 1571 } 1572 1573 type reqSameType2 struct { 1574 Foo1 *reqSameType `json:"foo1"` 1575 } 1576 1577 func TestBind_Issue1015(t *testing.T) { 1578 req := newMockRequest(). 1579 SetJSONContentType(). 1580 SetBody([]byte(`{"parent":{"parent":{}, "children":[{},{}], "foo1":{"foo1":{}}}, "children":[{},{}], "a":"asd"}`)) 1581 1582 var result reqSameType 1583 1584 err := DefaultBinder().Bind(req.Req, &result, nil) 1585 if err != nil { 1586 t.Error(err) 1587 } 1588 assert.NotNil(t, result.Parent) 1589 assert.NotNil(t, result.Parent.Parent) 1590 assert.Nil(t, result.Parent.Parent.Parent) 1591 assert.NotNil(t, result.Parent.Children) 1592 assert.DeepEqual(t, 2, len(result.Parent.Children)) 1593 assert.NotNil(t, result.Parent.Foo1.Foo1) 1594 assert.DeepEqual(t, "", result.Parent.A) 1595 assert.DeepEqual(t, 2, len(result.Children)) 1596 assert.Nil(t, result.Foo1.Foo1) 1597 assert.DeepEqual(t, "asd", result.A) 1598 } 1599 1600 func TestBind_JSONWithDefault(t *testing.T) { 1601 type Req struct { 1602 J1 string `json:"j1" default:"j1default"` 1603 } 1604 1605 req := newMockRequest(). 1606 SetJSONContentType(). 1607 SetBody([]byte(`{"j1":"j1"}`)) 1608 var result Req 1609 err := DefaultBinder().Bind(req.Req, &result, nil) 1610 if err != nil { 1611 t.Error(err) 1612 } 1613 assert.DeepEqual(t, "j1", result.J1) 1614 1615 result = Req{} 1616 req = newMockRequest(). 1617 SetJSONContentType(). 1618 SetBody([]byte(`{"j2":"j2"}`)) 1619 err = DefaultBinder().Bind(req.Req, &result, nil) 1620 if err != nil { 1621 t.Error(err) 1622 } 1623 assert.DeepEqual(t, "j1default", result.J1) 1624 } 1625 1626 func TestBind_WithoutPreBindForTag(t *testing.T) { 1627 type BaseQuery struct { 1628 Action string `query:"Action" binding:"required"` 1629 Version string `query:"Version" binding:"required"` 1630 } 1631 1632 req := newMockRequest(). 1633 SetJSONContentType(). 1634 SetRequestURI("http://foobar.com/?Action=action&Version=version"). 1635 SetBody([]byte(``)) 1636 1637 var result BaseQuery 1638 1639 err := DefaultBinder().BindQuery(req.Req, &result) 1640 if err != nil { 1641 t.Error(err) 1642 } 1643 assert.DeepEqual(t, "action", result.Action) 1644 assert.DeepEqual(t, "version", result.Version) 1645 } 1646 1647 func TestBind_NormalizeContentType(t *testing.T) { 1648 type BaseQuery struct { 1649 Action string `json:"action" binding:"required"` 1650 Version string `json:"version" binding:"required"` 1651 } 1652 1653 req := newMockRequest(). 1654 SetHeader("Content-Type", "ApplicAtion/json"). 1655 SetRequestURI("http://foobar.com/?Action=action&Version=version"). 1656 SetBody([]byte(`{"action":"action", "version":"version"}`)) 1657 1658 var result BaseQuery 1659 1660 err := DefaultBinder().BindQuery(req.Req, &result) 1661 if err != nil { 1662 t.Error(err) 1663 } 1664 assert.DeepEqual(t, "action", result.Action) 1665 assert.DeepEqual(t, "version", result.Version) 1666 } 1667 1668 func Benchmark_Binding(b *testing.B) { 1669 type Req struct { 1670 Version string `path:"v"` 1671 ID int `query:"id"` 1672 Header string `header:"h"` 1673 Form string `form:"f"` 1674 } 1675 1676 req := newMockRequest(). 1677 SetRequestURI("http://foobar.com?id=12"). 1678 SetHeaders("H", "header"). 1679 SetPostArg("f", "form"). 1680 SetUrlEncodeContentType() 1681 1682 var params param.Params 1683 params = append(params, param.Param{ 1684 Key: "v", 1685 Value: "1", 1686 }) 1687 1688 b.ResetTimer() 1689 for i := 0; i < b.N; i++ { 1690 var result Req 1691 err := DefaultBinder().Bind(req.Req, &result, params) 1692 if err != nil { 1693 b.Error(err) 1694 } 1695 if result.ID != 12 { 1696 b.Error("Id failed") 1697 } 1698 if result.Form != "form" { 1699 b.Error("form failed") 1700 } 1701 if result.Header != "header" { 1702 b.Error("header failed") 1703 } 1704 if result.Version != "1" { 1705 b.Error("path failed") 1706 } 1707 } 1708 }