gin/binding/binding_test.go
guonaihong f98b339b77 support bind http header param #1956 (#1957)
* support bind http header param #1956

update #1956
```
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

type testHeader struct {
	Rate   int    `header:"Rate"`
	Domain string `header:"Domain"`
}

func main() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		h := testHeader{}

		if err := c.ShouldBindHeader(&h); err != nil {
			c.JSON(200, err)
		}

		fmt.Printf("%#v\n", h)
		c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
	})

	r.Run()

// client
// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/
// output
// {"Domain":"music","Rate":300}
}
```

* add unit test

* Modify the code to get the http header

When the http header is obtained in the standard library,
the key value will be modified by the CanonicalMIMEHeaderKey function,
and finally the value of the http header will be obtained from the map.
As follows.
```go
func (h MIMEHeader) Get(key string) string {
        // ...
         v := h[CanonicalMIMEHeaderKey(key)]
        // ...
}
```

This pr also follows this modification

* Thanks to vkd for suggestions, modifying code

* Increase test coverage

env GOPATH=`pwd` go test github.com/gin-gonic/gin/binding -coverprofile=cover.prof
ok  	github.com/gin-gonic/gin/binding	0.015s	coverage: 100.0% of statements

* Rollback check code

* add use case to README.md
2019-06-27 12:47:45 +08:00

1209 lines
30 KiB
Go

// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"bytes"
"encoding/json"
"errors"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"strconv"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin/testdata/protoexample"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/ugorji/go/codec"
)
type appkey struct {
Appkey string `json:"appkey" form:"appkey"`
}
type QueryTest struct {
Page int `json:"page" form:"page"`
Size int `json:"size" form:"size"`
appkey
}
type FooStruct struct {
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required"`
}
type FooBarStruct struct {
FooStruct
Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"`
}
type FooBarFileStruct struct {
FooBarStruct
File *multipart.FileHeader `form:"file" binding:"required"`
}
type FooBarFileFailStruct struct {
FooBarStruct
File *multipart.FileHeader `invalid_name:"file" binding:"required"`
// for unexport test
data *multipart.FileHeader `form:"data" binding:"required"`
}
type FooDefaultBarStruct struct {
FooStruct
Bar string `msgpack:"bar" json:"bar" form:"bar,default=hello" xml:"bar" binding:"required"`
}
type FooStructUseNumber struct {
Foo interface{} `json:"foo" binding:"required"`
}
type FooBarStructForTimeType struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`
TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`
}
type FooStructForTimeTypeNotFormat struct {
TimeFoo time.Time `form:"time_foo"`
}
type FooStructForTimeTypeFailFormat struct {
TimeFoo time.Time `form:"time_foo" time_format:"2017-11-15"`
}
type FooStructForTimeTypeFailLocation struct {
TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_location:"/asia/chongqing"`
}
type FooStructForMapType struct {
MapFoo map[string]interface{} `form:"map_foo"`
}
type FooStructForIgnoreFormTag struct {
Foo *string `form:"-"`
}
type InvalidNameType struct {
TestName string `invalid_name:"test_name"`
}
type InvalidNameMapType struct {
TestName struct {
MapFoo map[string]interface{} `form:"map_foo"`
}
}
type FooStructForSliceType struct {
SliceFoo []int `form:"slice_foo"`
}
type FooStructForStructType struct {
StructFoo struct {
Idx int `form:"idx"`
}
}
type FooStructForStructPointerType struct {
StructPointerFoo *struct {
Name string `form:"name"`
}
}
type FooStructForSliceMapType struct {
// Unknown type: not support map
SliceMapFoo []map[string]interface{} `form:"slice_map_foo"`
}
type FooStructForBoolType struct {
BoolFoo bool `form:"bool_foo"`
}
type FooStructForStringPtrType struct {
PtrFoo *string `form:"ptr_foo"`
PtrBar *string `form:"ptr_bar" binding:"required"`
}
type FooStructForMapPtrType struct {
PtrBar *map[string]interface{} `form:"ptr_bar"`
}
func TestBindingDefault(t *testing.T) {
assert.Equal(t, Form, Default("GET", ""))
assert.Equal(t, Form, Default("GET", MIMEJSON))
assert.Equal(t, JSON, Default("POST", MIMEJSON))
assert.Equal(t, JSON, Default("PUT", MIMEJSON))
assert.Equal(t, XML, Default("POST", MIMEXML))
assert.Equal(t, XML, Default("PUT", MIMEXML2))
assert.Equal(t, Form, Default("POST", MIMEPOSTForm))
assert.Equal(t, Form, Default("PUT", MIMEPOSTForm))
assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm))
assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm))
assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF))
assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF))
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
assert.Equal(t, YAML, Default("POST", MIMEYAML))
assert.Equal(t, YAML, Default("PUT", MIMEYAML))
}
func TestBindingJSONNilBody(t *testing.T) {
var obj FooStruct
req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := JSON.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingJSON(t *testing.T) {
testBodyBinding(t,
JSON, "json",
"/", "/",
`{"foo": "bar"}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber(t *testing.T) {
testBodyBindingUseNumber(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber2(t *testing.T) {
testBodyBindingUseNumber2(t,
JSON, "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST",
"/", "/",
"foo=bar&bar=foo", "bar2=foo")
}
func TestBindingForm2(t *testing.T) {
testFormBinding(t, "GET",
"/?foo=bar&bar=foo", "/?bar2=foo",
"", "")
}
func TestBindingFormEmbeddedStruct(t *testing.T) {
testFormBindingEmbeddedStruct(t, "POST",
"/", "/",
"page=1&size=2&appkey=test-appkey", "bar2=foo")
}
func TestBindingFormEmbeddedStruct2(t *testing.T) {
testFormBindingEmbeddedStruct(t, "GET",
"/?page=1&size=2&appkey=test-appkey", "/?bar2=foo",
"", "")
}
func TestBindingFormDefaultValue(t *testing.T) {
testFormBindingDefaultValue(t, "POST",
"/", "/",
"foo=bar", "bar2=foo")
}
func TestBindingFormDefaultValue2(t *testing.T) {
testFormBindingDefaultValue(t, "GET",
"/?foo=bar", "/?bar2=foo",
"", "")
}
func TestBindingFormForTime(t *testing.T) {
testFormBindingForTime(t, "POST",
"/", "/",
"time_foo=2017-11-15&time_bar=", "bar2=foo")
testFormBindingForTimeNotFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailFormat(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailLocation(t, "POST",
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
}
func TestBindingFormForTime2(t *testing.T) {
testFormBindingForTime(t, "GET",
"/?time_foo=2017-11-15&time_bar=", "/?bar2=foo",
"", "")
testFormBindingForTimeNotFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailFormat(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailLocation(t, "GET",
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
}
func TestFormBindingIgnoreField(t *testing.T) {
testFormBindingIgnoreField(t, "POST",
"/", "/",
"-=bar", "")
}
func TestBindingFormInvalidName(t *testing.T) {
testFormBindingInvalidName(t, "POST",
"/", "/",
"test_name=bar", "bar2=foo")
}
func TestBindingFormInvalidName2(t *testing.T) {
testFormBindingInvalidName2(t, "POST",
"/", "/",
"map_foo=bar", "bar2=foo")
}
func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST",
"/", "/",
"map_foo={\"bar\":123}", "map_foo=1", "Map")
testFormBindingForType(t, "POST",
"/", "/",
"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
testFormBindingForType(t, "GET",
"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
"", "", "Slice")
testFormBindingForType(t, "POST",
"/", "/",
"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
testFormBindingForType(t, "GET",
"/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2",
"", "", "SliceMap")
testFormBindingForType(t, "POST",
"/", "/",
"ptr_bar=test", "bar2=test", "Ptr")
testFormBindingForType(t, "GET",
"/?ptr_bar=test", "/?bar2=test",
"", "", "Ptr")
testFormBindingForType(t, "POST",
"/", "/",
"idx=123", "id1=1", "Struct")
testFormBindingForType(t, "GET",
"/?idx=123", "/?id1=1",
"", "", "Struct")
testFormBindingForType(t, "POST",
"/", "/",
"name=thinkerou", "name1=ou", "StructPointer")
testFormBindingForType(t, "GET",
"/?name=thinkerou", "/?name1=ou",
"", "", "StructPointer")
}
func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST",
"/?foo=bar&bar=foo", "/",
"foo=unused", "bar2=foo")
}
func TestBindingQuery2(t *testing.T) {
testQueryBinding(t, "GET",
"/?foo=bar&bar=foo", "/?bar2=foo",
"foo=unused", "")
}
func TestBindingQueryFail(t *testing.T) {
testQueryBindingFail(t, "POST",
"/?map_foo=", "/",
"map_foo=unused", "bar2=foo")
}
func TestBindingQueryFail2(t *testing.T) {
testQueryBindingFail(t, "GET",
"/?map_foo=", "/?bar2=foo",
"map_foo=unused", "")
}
func TestBindingQueryBoolFail(t *testing.T) {
testQueryBindingBoolFail(t, "GET",
"/?bool_foo=fasl", "/?bar2=foo",
"bool_foo=unused", "")
}
func TestBindingXML(t *testing.T) {
testBodyBinding(t,
XML, "xml",
"/", "/",
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
}
func TestBindingXMLFail(t *testing.T) {
testBodyBindingFail(t,
XML, "xml",
"/", "/",
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
}
func TestBindingYAML(t *testing.T) {
testBodyBinding(t,
YAML, "yaml",
"/", "/",
`foo: bar`, `bar: foo`)
}
func TestBindingYAMLFail(t *testing.T) {
testBodyBindingFail(t,
YAML, "yaml",
"/", "/",
`foo:\nbar`, `bar: foo`)
}
func createFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createDefaultFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormPostRequestForMap(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormPostRequestForMapFail(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
}
func createFormFilesMultipartRequest(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("foo", "bar"))
assert.NoError(t, mw.WriteField("bar", "foo"))
f, err := os.Open("form.go")
assert.NoError(t, err)
defer f.Close()
fw, err1 := mw.CreateFormFile("file", "form.go")
assert.NoError(t, err1)
io.Copy(fw, f)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func createFormFilesMultipartRequestFail(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("foo", "bar"))
assert.NoError(t, mw.WriteField("bar", "foo"))
f, err := os.Open("form.go")
assert.NoError(t, err)
defer f.Close()
fw, err1 := mw.CreateFormFile("file_foo", "form_foo.go")
assert.NoError(t, err1)
io.Copy(fw, f)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func createFormMultipartRequest(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("foo", "bar"))
assert.NoError(t, mw.WriteField("bar", "foo"))
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func createFormMultipartRequestForMap(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
boundary := "--testboundary"
body := new(bytes.Buffer)
mw := multipart.NewWriter(body)
defer mw.Close()
assert.NoError(t, mw.SetBoundary(boundary))
assert.NoError(t, mw.WriteField("map_foo", "3.14"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
func TestBindingFormPost(t *testing.T) {
req := createFormPostRequest(t)
var obj FooBarStruct
assert.NoError(t, FormPost.Bind(req, &obj))
assert.Equal(t, "form-urlencoded", FormPost.Name())
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "foo", obj.Bar)
}
func TestBindingDefaultValueFormPost(t *testing.T) {
req := createDefaultFormPostRequest(t)
var obj FooDefaultBarStruct
assert.NoError(t, FormPost.Bind(req, &obj))
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "hello", obj.Bar)
}
func TestBindingFormPostForMap(t *testing.T) {
req := createFormPostRequestForMap(t)
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
}
func TestBindingFormPostForMapFail(t *testing.T) {
req := createFormPostRequestForMapFail(t)
var obj FooStructForMapType
err := FormPost.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingFormFilesMultipart(t *testing.T) {
req := createFormFilesMultipartRequest(t)
var obj FooBarFileStruct
FormMultipart.Bind(req, &obj)
// file from os
f, _ := os.Open("form.go")
defer f.Close()
fileActual, _ := ioutil.ReadAll(f)
// file from multipart
mf, _ := obj.File.Open()
defer mf.Close()
fileExpect, _ := ioutil.ReadAll(mf)
assert.Equal(t, FormMultipart.Name(), "multipart/form-data")
assert.Equal(t, obj.Foo, "bar")
assert.Equal(t, obj.Bar, "foo")
assert.Equal(t, fileExpect, fileActual)
}
func TestBindingFormFilesMultipartFail(t *testing.T) {
req := createFormFilesMultipartRequestFail(t)
var obj FooBarFileFailStruct
err := FormMultipart.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingFormMultipart(t *testing.T) {
req := createFormMultipartRequest(t)
var obj FooBarStruct
assert.NoError(t, FormMultipart.Bind(req, &obj))
assert.Equal(t, "multipart/form-data", FormMultipart.Name())
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "foo", obj.Bar)
}
func TestBindingFormMultipartForMap(t *testing.T) {
req := createFormMultipartRequestForMap(t)
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
assert.Equal(t, "thinkerou", obj.MapFoo["name"].(string))
assert.Equal(t, float64(3.14), obj.MapFoo["pai"].(float64))
}
func TestBindingFormMultipartForMapFail(t *testing.T) {
req := createFormMultipartRequestForMapFail(t)
var obj FooStructForMapType
err := FormMultipart.Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingProtoBuf(t *testing.T) {
test := &protoexample.Test{
Label: proto.String("yes"),
}
data, _ := proto.Marshal(test)
testProtoBodyBinding(t,
ProtoBuf, "protobuf",
"/", "/",
string(data), string(data[1:]))
}
func TestBindingProtoBufFail(t *testing.T) {
test := &protoexample.Test{
Label: proto.String("yes"),
}
data, _ := proto.Marshal(test)
testProtoBodyBindingFail(t,
ProtoBuf, "protobuf",
"/", "/",
string(data), string(data[1:]))
}
func TestBindingMsgPack(t *testing.T) {
test := FooStruct{
Foo: "bar",
}
h := new(codec.MsgpackHandle)
assert.NotNil(t, h)
buf := bytes.NewBuffer([]byte{})
assert.NotNil(t, buf)
err := codec.NewEncoder(buf, h).Encode(test)
assert.NoError(t, err)
data := buf.Bytes()
testMsgPackBodyBinding(t,
MsgPack, "msgpack",
"/", "/",
string(data), string(data[1:]))
}
func TestValidationFails(t *testing.T) {
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj)
assert.Error(t, err)
}
func TestValidationDisabled(t *testing.T) {
backup := Validator
Validator = nil
defer func() { Validator = backup }()
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj)
assert.NoError(t, err)
}
func TestExistsSucceeds(t *testing.T) {
type HogeStruct struct {
Hoge *int `json:"hoge" binding:"exists"`
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"hoge": 0}`)
err := JSON.Bind(req, &obj)
assert.NoError(t, err)
}
func TestExistsFails(t *testing.T) {
type HogeStruct struct {
Hoge *int `json:"foo" binding:"exists"`
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"boen": 0}`)
err := JSON.Bind(req, &obj)
assert.Error(t, err)
}
func TestHeaderBinding(t *testing.T) {
h := Header
assert.Equal(t, "header", h.Name())
type tHeader struct {
Limit int `header:"limit"`
}
var theader tHeader
req := requestWithBody("GET", "/", "")
req.Header.Add("limit", "1000")
assert.NoError(t, h.Bind(req, &theader))
assert.Equal(t, 1000, theader.Limit)
req = requestWithBody("GET", "/", "")
req.Header.Add("fail", `{fail:fail}`)
type failStruct struct {
Fail map[string]interface{} `header:"fail"`
}
err := h.Bind(req, &failStruct{})
assert.Error(t, err)
}
func TestUriBinding(t *testing.T) {
b := Uri
assert.Equal(t, "uri", b.Name())
type Tag struct {
Name string `uri:"name"`
}
var tag Tag
m := make(map[string][]string)
m["name"] = []string{"thinkerou"}
assert.NoError(t, b.BindUri(m, &tag))
assert.Equal(t, "thinkerou", tag.Name)
type NotSupportStruct struct {
Name map[string]interface{} `uri:"name"`
}
var not NotSupportStruct
assert.Error(t, b.BindUri(m, &not))
assert.Equal(t, map[string]interface{}(nil), not.Name)
}
func TestUriInnerBinding(t *testing.T) {
type Tag struct {
Name string `uri:"name"`
S struct {
Age int `uri:"age"`
}
}
expectedName := "mike"
expectedAge := 25
m := map[string][]string{
"name": {expectedName},
"age": {strconv.Itoa(expectedAge)},
}
var tag Tag
assert.NoError(t, Uri.BindUri(m, &tag))
assert.Equal(t, tag.Name, expectedName)
assert.Equal(t, tag.S.Age, expectedAge)
}
func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := QueryTest{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, 1, obj.Page)
assert.Equal(t, 2, obj.Size)
assert.Equal(t, "test-appkey", obj.Appkey)
}
func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "foo", obj.Bar)
obj = FooBarStruct{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooDefaultBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "hello", obj.Bar)
obj = FooDefaultBarStruct{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormBindingFail(t *testing.T) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormBindingMultipartFail(t *testing.T) {
obj := FooBarStruct{}
req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar"))
assert.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary")
_, err = req.MultipartReader()
assert.NoError(t, err)
err = Form.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormPostBindingFail(t *testing.T) {
b := FormPost
assert.Equal(t, "form-urlencoded", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func TestFormMultipartBindingFail(t *testing.T) {
b := FormMultipart
assert.Equal(t, "multipart/form-data", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooBarStructForTimeType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, int64(1510675200), obj.TimeFoo.Unix())
assert.Equal(t, "Asia/Chongqing", obj.TimeFoo.Location().String())
assert.Equal(t, int64(-62135596800), obj.TimeBar.Unix())
assert.Equal(t, "UTC", obj.TimeBar.Location().String())
obj = FooBarStructForTimeType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeNotFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailFormat{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailLocation{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := FooStructForIgnoreFormTag{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Nil(t, obj.Foo)
}
func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := InvalidNameType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "", obj.TestName)
obj = InvalidNameType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badBody string) {
b := Form
assert.Equal(t, "form", b.Name())
obj := InvalidNameMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = InvalidNameMapType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) {
b := Form
assert.Equal(t, "form", b.Name())
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
switch typ {
case "Slice":
obj := FooStructForSliceType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, []int{1, 2}, obj.SliceFoo)
obj = FooStructForSliceType{}
req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
case "Struct":
obj := FooStructForStructType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t,
struct {
Idx int "form:\"idx\""
}(struct {
Idx int "form:\"idx\""
}{Idx: 123}),
obj.StructFoo)
case "StructPointer":
obj := FooStructForStructPointerType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t,
struct {
Name string "form:\"name\""
}(struct {
Name string "form:\"name\""
}{Name: "thinkerou"}),
*obj.StructPointerFoo)
case "Map":
obj := FooStructForMapType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
case "SliceMap":
obj := FooStructForSliceMapType{}
err := b.Bind(req, &obj)
assert.Error(t, err)
case "Ptr":
obj := FooStructForStringPtrType{}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Nil(t, obj.PtrFoo)
assert.Equal(t, "test", *obj.PtrBar)
obj = FooStructForStringPtrType{}
obj.PtrBar = new(string)
err = b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "test", *obj.PtrBar)
objErr := FooStructForMapPtrType{}
err = b.Bind(req, &objErr)
assert.Error(t, err)
obj = FooStructForStringPtrType{}
req = requestWithBody(method, badPath, badBody)
err = b.Bind(req, &obj)
assert.Error(t, err)
}
}
func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, "query", b.Name())
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
assert.Equal(t, "foo", obj.Bar)
}
func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, "query", b.Name())
obj := FooStructForMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody string) {
b := Query
assert.Equal(t, "query", b.Name())
obj := FooStructForBoolType{}
req := requestWithBody(method, path, body)
if method == "POST" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = true
err := b.Bind(req, &obj)
assert.NoError(t, err)
// we hope it is int64(123)
v, e := obj.Foo.(json.Number).Int64()
assert.NoError(t, e)
assert.Equal(t, int64(123), v)
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
EnableDecoderUseNumber = false
err := b.Bind(req, &obj)
assert.NoError(t, err)
// it will return float64(123) if not use EnableDecoderUseNumber
// maybe it is not hoped
assert.Equal(t, float64(123), obj.Foo)
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
err := b.Bind(req, &obj)
assert.Error(t, err)
assert.Equal(t, "", obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
err = JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := protoexample.Test{}
req := requestWithBody("POST", path, body)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "yes", *obj.Label)
obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
assert.Error(t, err)
}
type hook struct{}
func (h hook) Read([]byte) (int, error) {
return 0, errors.New("error")
}
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := protoexample.Test{}
req := requestWithBody("POST", path, body)
req.Body = ioutil.NopCloser(&hook{})
req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
assert.Error(t, err)
}
func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
req.Header.Add("Content-Type", MIMEMSGPACK)
err := b.Bind(req, &obj)
assert.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
req.Header.Add("Content-Type", MIMEMSGPACK)
err = MsgPack.Bind(req, &obj)
assert.Error(t, err)
}
func requestWithBody(method, path, body string) (req *http.Request) {
req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
return
}