Merge tag 'tags/v1.11.0'

This commit is contained in:
2025-10-14 13:53:17 +03:00
72 changed files with 2578 additions and 930 deletions
+5 -4
View File
@@ -8,6 +8,7 @@ package binding
import (
"bytes"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
@@ -39,20 +40,20 @@ func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body,
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
req.Header.Add("Content-Type", MIMEMSGPACK)
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEMSGPACK)
err = MsgPack.Bind(req, &obj)
require.Error(t, err)
}
func TestBindingDefaultMsgPack(t *testing.T) {
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
assert.Equal(t, MsgPack, Default(http.MethodPost, MIMEMSGPACK))
assert.Equal(t, MsgPack, Default(http.MethodPut, MIMEMSGPACK2))
}
+134 -130
View File
@@ -51,8 +51,6 @@ type FooBarFileStruct struct {
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 {
@@ -69,15 +67,19 @@ type FooStructDisallowUnknownFields struct {
}
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"`
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
UnixTime time.Time `form:"unixTime" time_format:"unix"`
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"`
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
UnixTime time.Time `form:"unixTime" time_format:"unix"`
UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixmilli"`
UnixMicroTime time.Time `form:"unixMicroTime" time_format:"uNiXmiCrO"`
}
type FooStructForTimeTypeNotUnixFormat struct {
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
UnixTime time.Time `form:"unixTime" time_format:"unix"`
CreateTime time.Time `form:"createTime" time_format:"unixNano"`
UnixTime time.Time `form:"unixTime" time_format:"unix"`
UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixMilli"`
UnixMicroTime time.Time `form:"unixMicroTime" time_format:"unixMicro"`
}
type FooStructForTimeTypeNotFormat struct {
@@ -145,31 +147,31 @@ type FooStructForMapPtrType struct {
}
func TestBindingDefault(t *testing.T) {
assert.Equal(t, Form, Default("GET", ""))
assert.Equal(t, Form, Default("GET", MIMEJSON))
assert.Equal(t, Form, Default(http.MethodGet, ""))
assert.Equal(t, Form, Default(http.MethodGet, MIMEJSON))
assert.Equal(t, JSON, Default("POST", MIMEJSON))
assert.Equal(t, JSON, Default("PUT", MIMEJSON))
assert.Equal(t, JSON, Default(http.MethodPost, MIMEJSON))
assert.Equal(t, JSON, Default(http.MethodPut, MIMEJSON))
assert.Equal(t, XML, Default("POST", MIMEXML))
assert.Equal(t, XML, Default("PUT", MIMEXML2))
assert.Equal(t, XML, Default(http.MethodPost, MIMEXML))
assert.Equal(t, XML, Default(http.MethodPut, MIMEXML2))
assert.Equal(t, Form, Default("POST", MIMEPOSTForm))
assert.Equal(t, Form, Default("PUT", MIMEPOSTForm))
assert.Equal(t, Form, Default(http.MethodPost, MIMEPOSTForm))
assert.Equal(t, Form, Default(http.MethodPut, MIMEPOSTForm))
assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm))
assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm))
assert.Equal(t, FormMultipart, Default(http.MethodPost, MIMEMultipartPOSTForm))
assert.Equal(t, FormMultipart, Default(http.MethodPut, MIMEMultipartPOSTForm))
assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF))
assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF))
assert.Equal(t, ProtoBuf, Default(http.MethodPost, MIMEPROTOBUF))
assert.Equal(t, ProtoBuf, Default(http.MethodPut, MIMEPROTOBUF))
assert.Equal(t, YAML, Default("POST", MIMEYAML))
assert.Equal(t, YAML, Default("PUT", MIMEYAML))
assert.Equal(t, YAML, Default("POST", MIMEYAML2))
assert.Equal(t, YAML, Default("PUT", MIMEYAML2))
assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML))
assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML))
assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML2))
assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML2))
assert.Equal(t, TOML, Default("POST", MIMETOML))
assert.Equal(t, TOML, Default("PUT", MIMETOML))
assert.Equal(t, TOML, Default(http.MethodPost, MIMETOML))
assert.Equal(t, TOML, Default(http.MethodPut, MIMETOML))
}
func TestBindingJSONNilBody(t *testing.T) {
@@ -227,137 +229,137 @@ func TestBindingJSONStringMap(t *testing.T) {
}
func TestBindingForm(t *testing.T) {
testFormBinding(t, "POST",
testFormBinding(t, http.MethodPost,
"/", "/",
"foo=bar&bar=foo", "bar2=foo")
}
func TestBindingForm2(t *testing.T) {
testFormBinding(t, "GET",
testFormBinding(t, http.MethodGet,
"/?foo=bar&bar=foo", "/?bar2=foo",
"", "")
}
func TestBindingFormEmbeddedStruct(t *testing.T) {
testFormBindingEmbeddedStruct(t, "POST",
testFormBindingEmbeddedStruct(t, http.MethodPost,
"/", "/",
"page=1&size=2&appkey=test-appkey", "bar2=foo")
}
func TestBindingFormEmbeddedStruct2(t *testing.T) {
testFormBindingEmbeddedStruct(t, "GET",
testFormBindingEmbeddedStruct(t, http.MethodGet,
"/?page=1&size=2&appkey=test-appkey", "/?bar2=foo",
"", "")
}
func TestBindingFormDefaultValue(t *testing.T) {
testFormBindingDefaultValue(t, "POST",
testFormBindingDefaultValue(t, http.MethodPost,
"/", "/",
"foo=bar", "bar2=foo")
}
func TestBindingFormDefaultValue2(t *testing.T) {
testFormBindingDefaultValue(t, "GET",
testFormBindingDefaultValue(t, http.MethodGet,
"/?foo=bar", "/?bar2=foo",
"", "")
}
func TestBindingFormForTime(t *testing.T) {
testFormBindingForTime(t, "POST",
testFormBindingForTime(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo")
testFormBindingForTimeNotUnixFormat(t, "POST",
"time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "bar2=foo")
testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, "POST",
"time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailFormat(t, "POST",
testFormBindingForTimeFailFormat(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
testFormBindingForTimeFailLocation(t, "POST",
testFormBindingForTimeFailLocation(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15", "bar2=foo")
}
func TestBindingFormForTime2(t *testing.T) {
testFormBindingForTime(t, "GET",
"/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo",
testFormBindingForTime(t, http.MethodGet,
"/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "/?bar2=foo",
"", "")
testFormBindingForTimeNotUnixFormat(t, "POST",
testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
"/", "/",
"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, "GET",
"time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo")
testFormBindingForTimeNotFormat(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailFormat(t, "GET",
testFormBindingForTimeFailFormat(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
testFormBindingForTimeFailLocation(t, "GET",
testFormBindingForTimeFailLocation(t, http.MethodGet,
"/?time_foo=2017-11-15", "/?bar2=foo",
"", "")
}
func TestFormBindingIgnoreField(t *testing.T) {
testFormBindingIgnoreField(t, "POST",
testFormBindingIgnoreField(t, http.MethodPost,
"/", "/",
"-=bar", "")
}
func TestBindingFormInvalidName(t *testing.T) {
testFormBindingInvalidName(t, "POST",
testFormBindingInvalidName(t, http.MethodPost,
"/", "/",
"test_name=bar", "bar2=foo")
}
func TestBindingFormInvalidName2(t *testing.T) {
testFormBindingInvalidName2(t, "POST",
testFormBindingInvalidName2(t, http.MethodPost,
"/", "/",
"map_foo=bar", "bar2=foo")
}
func TestBindingFormForType(t *testing.T) {
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"map_foo={\"bar\":123}", "map_foo=1", "Map")
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
testFormBindingForType(t, "GET",
testFormBindingForType(t, http.MethodGet,
"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
"", "", "Slice")
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
testFormBindingForType(t, "GET",
testFormBindingForType(t, http.MethodGet,
"/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2",
"", "", "SliceMap")
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"ptr_bar=test", "bar2=test", "Ptr")
testFormBindingForType(t, "GET",
testFormBindingForType(t, http.MethodGet,
"/?ptr_bar=test", "/?bar2=test",
"", "", "Ptr")
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"idx=123", "id1=1", "Struct")
testFormBindingForType(t, "GET",
testFormBindingForType(t, http.MethodGet,
"/?idx=123", "/?id1=1",
"", "", "Struct")
testFormBindingForType(t, "POST",
testFormBindingForType(t, http.MethodPost,
"/", "/",
"name=thinkerou", "name1=ou", "StructPointer")
testFormBindingForType(t, "GET",
testFormBindingForType(t, http.MethodGet,
"/?name=thinkerou", "/?name1=ou",
"", "", "StructPointer")
}
@@ -374,7 +376,7 @@ func TestBindingFormStringMap(t *testing.T) {
func TestBindingFormStringSliceMap(t *testing.T) {
obj := make(map[string][]string)
req := requestWithBody("POST", "/", "foo=something&foo=bar&hello=world")
req := requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
req.Header.Add("Content-Type", MIMEPOSTForm)
err := Form.Bind(req, &obj)
require.NoError(t, err)
@@ -387,38 +389,38 @@ func TestBindingFormStringSliceMap(t *testing.T) {
assert.True(t, reflect.DeepEqual(obj, target))
objInvalid := make(map[string][]int)
req = requestWithBody("POST", "/", "foo=something&foo=bar&hello=world")
req = requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
req.Header.Add("Content-Type", MIMEPOSTForm)
err = Form.Bind(req, &objInvalid)
require.Error(t, err)
}
func TestBindingQuery(t *testing.T) {
testQueryBinding(t, "POST",
testQueryBinding(t, http.MethodPost,
"/?foo=bar&bar=foo", "/",
"foo=unused", "bar2=foo")
}
func TestBindingQuery2(t *testing.T) {
testQueryBinding(t, "GET",
testQueryBinding(t, http.MethodGet,
"/?foo=bar&bar=foo", "/?bar2=foo",
"foo=unused", "")
}
func TestBindingQueryFail(t *testing.T) {
testQueryBindingFail(t, "POST",
testQueryBindingFail(t, http.MethodPost,
"/?map_foo=", "/",
"map_foo=unused", "bar2=foo")
}
func TestBindingQueryFail2(t *testing.T) {
testQueryBindingFail(t, "GET",
testQueryBindingFail(t, http.MethodGet,
"/?map_foo=", "/?bar2=foo",
"map_foo=unused", "")
}
func TestBindingQueryBoolFail(t *testing.T) {
testQueryBindingBoolFail(t, "GET",
testQueryBindingBoolFail(t, http.MethodGet,
"/?bool_foo=fasl", "/?bar2=foo",
"bool_foo=unused", "")
}
@@ -427,7 +429,7 @@ func TestBindingQueryStringMap(t *testing.T) {
b := Query
obj := make(map[string]string)
req := requestWithBody("GET", "/?foo=bar&hello=world", "")
req := requestWithBody(http.MethodGet, "/?foo=bar&hello=world", "")
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.NotNil(t, obj)
@@ -436,7 +438,7 @@ func TestBindingQueryStringMap(t *testing.T) {
assert.Equal(t, "world", obj["hello"])
obj = make(map[string]string)
req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last
req = requestWithBody(http.MethodGet, "/?foo=bar&foo=2&hello=world", "") // should pick last
err = b.Bind(req, &obj)
require.NoError(t, err)
assert.NotNil(t, obj)
@@ -495,28 +497,28 @@ func TestBindingYAMLFail(t *testing.T) {
}
func createFormPostRequest(t *testing.T) *http.Request {
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
require.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"))
req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
require.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}"))
req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
require.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"))
req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEPOSTForm)
return req
@@ -540,7 +542,7 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request {
_, err = io.Copy(fw, f)
require.NoError(t, err)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
@@ -565,7 +567,7 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request {
_, err = io.Copy(fw, f)
require.NoError(t, err)
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err2)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
@@ -581,7 +583,7 @@ func createFormMultipartRequest(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("foo", "bar"))
require.NoError(t, mw.WriteField("bar", "foo"))
req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
@@ -595,7 +597,7 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
@@ -609,7 +611,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
require.NoError(t, mw.SetBoundary(boundary))
require.NoError(t, mw.WriteField("map_foo", "3.14"))
req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
@@ -731,7 +733,7 @@ func TestBindingProtoBufFail(t *testing.T) {
func TestValidationFails(t *testing.T) {
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -742,7 +744,7 @@ func TestValidationDisabled(t *testing.T) {
defer func() { Validator = backup }()
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
err := JSON.Bind(req, &obj)
require.NoError(t, err)
}
@@ -753,7 +755,7 @@ func TestRequiredSucceeds(t *testing.T) {
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"hoge": 0}`)
req := requestWithBody(http.MethodPost, "/", `{"hoge": 0}`)
err := JSON.Bind(req, &obj)
require.NoError(t, err)
}
@@ -764,7 +766,7 @@ func TestRequiredFails(t *testing.T) {
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"boen": 0}`)
req := requestWithBody(http.MethodPost, "/", `{"boen": 0}`)
err := JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -778,12 +780,12 @@ func TestHeaderBinding(t *testing.T) {
}
var theader tHeader
req := requestWithBody("GET", "/", "")
req := requestWithBody(http.MethodGet, "/", "")
req.Header.Add("limit", "1000")
require.NoError(t, h.Bind(req, &theader))
assert.Equal(t, 1000, theader.Limit)
req = requestWithBody("GET", "/", "")
req = requestWithBody(http.MethodGet, "/", "")
req.Header.Add("fail", `{fail:fail}`)
type failStruct struct {
@@ -843,7 +845,7 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba
obj := QueryTest{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -859,7 +861,7 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -879,7 +881,7 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB
obj := FooDefaultBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -898,14 +900,14 @@ func TestFormBindingFail(t *testing.T) {
assert.Equal(t, "form", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj)
require.Error(t, err)
}
func TestFormBindingMultipartFail(t *testing.T) {
obj := FooBarStruct{}
req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar"))
req, err := http.NewRequest(http.MethodPost, "/", strings.NewReader("foo=bar"))
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary")
_, err = req.MultipartReader()
@@ -919,7 +921,7 @@ func TestFormPostBindingFail(t *testing.T) {
assert.Equal(t, "form-urlencoded", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj)
require.Error(t, err)
}
@@ -929,7 +931,7 @@ func TestFormMultipartBindingFail(t *testing.T) {
assert.Equal(t, "multipart/form-data", b.Name())
obj := FooBarStruct{}
req, _ := http.NewRequest("POST", "/", nil)
req, _ := http.NewRequest(http.MethodPost, "/", nil)
err := b.Bind(req, &obj)
require.Error(t, err)
}
@@ -940,7 +942,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
obj := FooBarStructForTimeType{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -952,6 +954,8 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
assert.Equal(t, "UTC", obj.TimeBar.Location().String())
assert.Equal(t, int64(1562400033000000123), obj.CreateTime.UnixNano())
assert.Equal(t, int64(1562400033), obj.UnixTime.Unix())
assert.Equal(t, int64(1562400033001), obj.UnixMilliTime.UnixMilli())
assert.Equal(t, int64(1562400033000012), obj.UnixMicroTime.UnixMicro())
obj = FooBarStructForTimeType{}
req = requestWithBody(method, badPath, badBody)
@@ -965,7 +969,7 @@ func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, bo
obj := FooStructForTimeTypeNotUnixFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -983,7 +987,7 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1001,7 +1005,7 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1019,7 +1023,7 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod
obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1037,7 +1041,7 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo
obj := FooStructForIgnoreFormTag{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1052,12 +1056,12 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
obj := InvalidNameType{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.Equal(t, "", obj.TestName)
assert.Empty(t, obj.TestName)
obj = InvalidNameType{}
req = requestWithBody(method, badPath, badBody)
@@ -1071,7 +1075,7 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB
obj := InvalidNameMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1088,7 +1092,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
assert.Equal(t, "form", b.Name())
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
switch typ {
@@ -1159,7 +1163,7 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1174,7 +1178,7 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str
obj := FooStructForMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1187,7 +1191,7 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody
obj := FooStructForBoolType{}
req := requestWithBody(method, path, body)
if method == "POST" {
if method == http.MethodPost {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -1198,13 +1202,13 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -1213,19 +1217,19 @@ func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Equal(t, name, b.Name())
var obj1 []FooStruct
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj1)
require.NoError(t, err)
var obj2 []FooStruct
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj2)
require.Error(t, err)
}
func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
obj := make(map[string]string)
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
if b.Name() == "form" {
req.Header.Add("Content-Type", MIMEPOSTForm)
}
@@ -1238,13 +1242,13 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB
if badPath != "" && badBody != "" {
obj = make(map[string]string)
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = b.Bind(req, &obj)
require.Error(t, err)
}
objInt := make(map[string]int)
req = requestWithBody("POST", path, body)
req = requestWithBody(http.MethodPost, path, body)
err = b.Bind(req, &objInt)
require.Error(t, err)
}
@@ -1253,7 +1257,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
EnableDecoderUseNumber = true
err := b.Bind(req, &obj)
require.NoError(t, err)
@@ -1263,7 +1267,7 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, int64(123), v)
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -1272,7 +1276,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
EnableDecoderUseNumber = false
err := b.Bind(req, &obj)
require.NoError(t, err)
@@ -1281,7 +1285,7 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod
assert.InDelta(t, float64(123), obj.Foo, 0.01)
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -1293,13 +1297,13 @@ func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath
}()
obj := FooStructDisallowUnknownFields{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.Equal(t, "bar", obj.Foo)
obj = FooStructDisallowUnknownFields{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj)
require.Error(t, err)
assert.Contains(t, err.Error(), "what")
@@ -1309,13 +1313,13 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad
assert.Equal(t, name, b.Name())
obj := FooStruct{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
err := b.Bind(req, &obj)
require.Error(t, err)
assert.Equal(t, "", obj.Foo)
assert.Empty(t, obj.Foo)
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
err = JSON.Bind(req, &obj)
require.Error(t, err)
}
@@ -1324,14 +1328,14 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
assert.Equal(t, name, b.Name())
obj := protoexample.Test{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err := b.Bind(req, &obj)
require.NoError(t, err)
assert.Equal(t, "yes", *obj.Label)
obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
require.Error(t, err)
@@ -1358,28 +1362,28 @@ func TestPlainBinding(t *testing.T) {
assert.Equal(t, "plain", p.Name())
var s string
req := requestWithBody("POST", "/", "test string")
req := requestWithBody(http.MethodPost, "/", "test string")
require.NoError(t, p.Bind(req, &s))
assert.Equal(t, "test string", s)
var bs []byte
req = requestWithBody("POST", "/", "test []byte")
req = requestWithBody(http.MethodPost, "/", "test []byte")
require.NoError(t, p.Bind(req, &bs))
assert.Equal(t, bs, []byte("test []byte"))
var i int
req = requestWithBody("POST", "/", "test fail")
req = requestWithBody(http.MethodPost, "/", "test fail")
require.Error(t, p.Bind(req, &i))
req = requestWithBody("POST", "/", "")
req = requestWithBody(http.MethodPost, "/", "")
req.Body = &failRead{}
require.Error(t, p.Bind(req, &s))
req = requestWithBody("POST", "/", "")
req = requestWithBody(http.MethodPost, "/", "")
require.NoError(t, p.Bind(req, nil))
var ptr *string
req = requestWithBody("POST", "/", "")
req = requestWithBody(http.MethodPost, "/", "")
require.NoError(t, p.Bind(req, ptr))
}
@@ -1387,7 +1391,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, name, b.Name())
obj := protoexample.Test{}
req := requestWithBody("POST", path, body)
req := requestWithBody(http.MethodPost, path, body)
req.Body = io.NopCloser(&hook{})
req.Header.Add("Content-Type", MIMEPROTOBUF)
@@ -1402,7 +1406,7 @@ func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body
assert.Equal(t, "obj is not ProtoMessage", err.Error())
obj = protoexample.Test{}
req = requestWithBody("POST", badPath, badBody)
req = requestWithBody(http.MethodPost, badPath, badBody)
req.Header.Add("Content-Type", MIMEPROTOBUF)
err = ProtoBuf.Bind(req, &obj)
require.Error(t, err)
+4 -2
View File
@@ -18,14 +18,16 @@ func TestSliceValidationError(t *testing.T) {
{"has nil elements", SliceValidationError{errors.New("test error"), nil}, "[0]: test error"},
{"has zero elements", SliceValidationError{}, ""},
{"has one element", SliceValidationError{errors.New("test one error")}, "[0]: test one error"},
{"has two elements",
{
"has two elements",
SliceValidationError{
errors.New("first error"),
errors.New("second error"),
},
"[0]: first error\n[1]: second error",
},
{"has many elements",
{
"has many elements",
SliceValidationError{
errors.New("first error"),
errors.New("second error"),
+5 -3
View File
@@ -11,9 +11,11 @@ import (
const defaultMemory = 32 << 20
type formBinding struct{}
type formPostBinding struct{}
type formMultipartBinding struct{}
type (
formBinding struct{}
formPostBinding struct{}
formMultipartBinding struct{}
)
func (formBinding) Name() string {
return "form"
+15 -9
View File
@@ -13,8 +13,8 @@ import (
"strings"
"time"
"git.company.lan/gopkg/gin/codec/json"
"git.company.lan/gopkg/gin/internal/bytesconv"
"git.company.lan/gopkg/gin/internal/json"
)
var (
@@ -175,7 +175,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
type BindUnmarshaler interface {
// UnmarshalParam decodes and assigns a value from an form or query param.
// UnmarshalParam decodes and assigns a value from a form or query param.
UnmarshalParam(param string) error
}
@@ -333,9 +333,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
case multipart.FileHeader:
return nil
}
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
return json.API.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
case reflect.Map:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
return json.API.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
case reflect.Ptr:
if !value.Elem().IsValid() {
value.Set(reflect.New(value.Type().Elem()))
@@ -398,18 +398,24 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
}
switch tf := strings.ToLower(timeFormat); tf {
case "unix", "unixnano":
case "unix", "unixmilli", "unixmicro", "unixnano":
tv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
d := time.Duration(1)
if tf == "unixnano" {
d = time.Second
var t time.Time
switch tf {
case "unix":
t = time.Unix(tv, 0)
case "unixmilli":
t = time.UnixMilli(tv)
case "unixmicro":
t = time.UnixMicro(tv)
default:
t = time.Unix(0, tv)
}
t := time.Unix(tv/int64(d), tv%int64(d))
value.Set(reflect.ValueOf(t))
return nil
}
+22 -22
View File
@@ -6,7 +6,7 @@ package binding
import (
"encoding/hex"
"fmt"
"errors"
"mime/multipart"
"reflect"
"strconv"
@@ -494,7 +494,7 @@ type customUnmarshalParamType struct {
func (f *customUnmarshalParamType) UnmarshalParam(param string) error {
parts := strings.Split(param, ":")
if len(parts) != 3 {
return fmt.Errorf("invalid format")
return errors.New("invalid format")
}
f.Protocol = parts[0]
f.Path = parts[1]
@@ -509,9 +509,9 @@ func TestMappingCustomStructTypeWithFormTag(t *testing.T) {
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
require.NoError(t, err)
assert.EqualValues(t, "file", s.FileData.Protocol)
assert.EqualValues(t, "/foo", s.FileData.Path)
assert.EqualValues(t, "happiness", s.FileData.Name)
assert.Equal(t, "file", s.FileData.Protocol)
assert.Equal(t, "/foo", s.FileData.Path)
assert.Equal(t, "happiness", s.FileData.Name)
}
func TestMappingCustomStructTypeWithURITag(t *testing.T) {
@@ -521,9 +521,9 @@ func TestMappingCustomStructTypeWithURITag(t *testing.T) {
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
require.NoError(t, err)
assert.EqualValues(t, "file", s.FileData.Protocol)
assert.EqualValues(t, "/foo", s.FileData.Path)
assert.EqualValues(t, "happiness", s.FileData.Name)
assert.Equal(t, "file", s.FileData.Protocol)
assert.Equal(t, "/foo", s.FileData.Path)
assert.Equal(t, "happiness", s.FileData.Name)
}
func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
@@ -533,9 +533,9 @@ func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
require.NoError(t, err)
assert.EqualValues(t, "file", s.FileData.Protocol)
assert.EqualValues(t, "/foo", s.FileData.Path)
assert.EqualValues(t, "happiness", s.FileData.Name)
assert.Equal(t, "file", s.FileData.Protocol)
assert.Equal(t, "/foo", s.FileData.Path)
assert.Equal(t, "happiness", s.FileData.Name)
}
func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
@@ -545,9 +545,9 @@ func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
require.NoError(t, err)
assert.EqualValues(t, "file", s.FileData.Protocol)
assert.EqualValues(t, "/foo", s.FileData.Path)
assert.EqualValues(t, "happiness", s.FileData.Name)
assert.Equal(t, "file", s.FileData.Protocol)
assert.Equal(t, "/foo", s.FileData.Path)
assert.Equal(t, "happiness", s.FileData.Name)
}
type customPath []string
@@ -556,7 +556,7 @@ func (p *customPath) UnmarshalParam(param string) error {
elems := strings.Split(param, "/")
n := len(elems)
if n < 2 {
return fmt.Errorf("invalid format")
return errors.New("invalid format")
}
*p = elems
@@ -570,8 +570,8 @@ func TestMappingCustomSliceUri(t *testing.T) {
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri")
require.NoError(t, err)
assert.EqualValues(t, "bar", s.FileData[0])
assert.EqualValues(t, "foo", s.FileData[1])
assert.Equal(t, "bar", s.FileData[0])
assert.Equal(t, "foo", s.FileData[1])
}
func TestMappingCustomSliceForm(t *testing.T) {
@@ -581,8 +581,8 @@ func TestMappingCustomSliceForm(t *testing.T) {
err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form")
require.NoError(t, err)
assert.EqualValues(t, "bar", s.FileData[0])
assert.EqualValues(t, "foo", s.FileData[1])
assert.Equal(t, "bar", s.FileData[0])
assert.Equal(t, "foo", s.FileData[1])
}
type objectID [12]byte
@@ -600,7 +600,7 @@ func (o *objectID) UnmarshalParam(param string) error {
func convertTo(s string) (objectID, error) {
var nilObjectID objectID
if len(s) != 24 {
return nilObjectID, fmt.Errorf("invalid format")
return nilObjectID, errors.New("invalid format")
}
var oid [12]byte
@@ -621,7 +621,7 @@ func TestMappingCustomArrayUri(t *testing.T) {
require.NoError(t, err)
expected, _ := convertTo(val)
assert.EqualValues(t, expected, s.FileData)
assert.Equal(t, expected, s.FileData)
}
func TestMappingCustomArrayForm(t *testing.T) {
@@ -633,5 +633,5 @@ func TestMappingCustomArrayForm(t *testing.T) {
require.NoError(t, err)
expected, _ := convertTo(val)
assert.EqualValues(t, expected, s.FileData)
assert.Equal(t, expected, s.FileData)
}
-1
View File
@@ -17,7 +17,6 @@ func (headerBinding) Name() string {
}
func (headerBinding) Bind(req *http.Request, obj any) error {
if err := mapHeader(obj, req.Header); err != nil {
return err
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"io"
"net/http"
"git.company.lan/gopkg/gin/internal/json"
"git.company.lan/gopkg/gin/codec/json"
)
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
@@ -42,7 +42,7 @@ func (jsonBinding) BindBody(body []byte, obj any) error {
}
func decodeJSON(r io.Reader, obj any) error {
decoder := json.NewDecoder(r)
decoder := json.API.NewDecoder(r)
if EnableDecoderUseNumber {
decoder.UseNumber()
}
+186
View File
@@ -5,8 +5,16 @@
package binding
import (
"io"
"net/http/httptest"
"testing"
"time"
"unsafe"
"git.company.lan/gopkg/gin/codec/json"
"git.company.lan/gopkg/gin/render"
jsoniter "github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -28,3 +36,181 @@ func TestJSONBindingBindBodyMap(t *testing.T) {
assert.Equal(t, "FOO", s["foo"])
assert.Equal(t, "world", s["hello"])
}
func TestCustomJsonCodec(t *testing.T) {
// Restore json encoding configuration after testing
oldMarshal := json.API
defer func() {
json.API = oldMarshal
}()
// Custom json api
json.API = customJsonApi{}
// test decode json
obj := customReq{}
err := jsonBinding{}.BindBody([]byte(`{"time_empty":null,"time_struct": "2001-12-05 10:01:02.345","time_nil":null,"time_pointer":"2002-12-05 10:01:02.345"}`), &obj)
require.NoError(t, err)
assert.Equal(t, zeroTime, obj.TimeEmpty)
assert.Equal(t, time.Date(2001, 12, 5, 10, 1, 2, 345000000, time.Local), obj.TimeStruct)
assert.Nil(t, obj.TimeNil)
assert.Equal(t, time.Date(2002, 12, 5, 10, 1, 2, 345000000, time.Local), *obj.TimePointer)
// test encode json
w := httptest.NewRecorder()
err2 := (render.PureJSON{Data: obj}).Render(w)
require.NoError(t, err2)
assert.JSONEq(t, "{\"time_empty\":null,\"time_struct\":\"2001-12-05 10:01:02.345\",\"time_nil\":null,\"time_pointer\":\"2002-12-05 10:01:02.345\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
}
type customReq struct {
TimeEmpty time.Time `json:"time_empty"`
TimeStruct time.Time `json:"time_struct"`
TimeNil *time.Time `json:"time_nil"`
TimePointer *time.Time `json:"time_pointer"`
}
var customConfig = jsoniter.Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
}.Froze()
func init() {
customConfig.RegisterExtension(&TimeEx{})
customConfig.RegisterExtension(&TimePointerEx{})
}
type customJsonApi struct{}
func (j customJsonApi) Marshal(v any) ([]byte, error) {
return customConfig.Marshal(v)
}
func (j customJsonApi) Unmarshal(data []byte, v any) error {
return customConfig.Unmarshal(data, v)
}
func (j customJsonApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
return customConfig.MarshalIndent(v, prefix, indent)
}
func (j customJsonApi) NewEncoder(writer io.Writer) json.Encoder {
return customConfig.NewEncoder(writer)
}
func (j customJsonApi) NewDecoder(reader io.Reader) json.Decoder {
return customConfig.NewDecoder(reader)
}
// region Time Extension
var (
zeroTime = time.Time{}
timeType = reflect2.TypeOfPtr((*time.Time)(nil)).Elem()
defaultTimeCodec = &timeCodec{}
)
type TimeEx struct {
jsoniter.DummyExtension
}
func (te *TimeEx) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ == timeType {
return defaultTimeCodec
}
return nil
}
func (te *TimeEx) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ == timeType {
return defaultTimeCodec
}
return nil
}
type timeCodec struct{}
func (tc timeCodec) IsEmpty(ptr unsafe.Pointer) bool {
t := *((*time.Time)(ptr))
return t.Equal(zeroTime)
}
func (tc timeCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
t := *((*time.Time)(ptr))
if t.Equal(zeroTime) {
stream.WriteNil()
return
}
stream.WriteString(t.In(time.Local).Format("2006-01-02 15:04:05.000"))
}
func (tc timeCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
ts := iter.ReadString()
if len(ts) == 0 {
*((*time.Time)(ptr)) = zeroTime
return
}
t, err := time.ParseInLocation("2006-01-02 15:04:05.000", ts, time.Local)
if err != nil {
panic(err)
}
*((*time.Time)(ptr)) = t
}
// endregion
// region *Time Extension
var (
timePointerType = reflect2.TypeOfPtr((**time.Time)(nil)).Elem()
defaultTimePointerCodec = &timePointerCodec{}
)
type TimePointerEx struct {
jsoniter.DummyExtension
}
func (tpe *TimePointerEx) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
if typ == timePointerType {
return defaultTimePointerCodec
}
return nil
}
func (tpe *TimePointerEx) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
if typ == timePointerType {
return defaultTimePointerCodec
}
return nil
}
type timePointerCodec struct{}
func (tpc timePointerCodec) IsEmpty(ptr unsafe.Pointer) bool {
t := *((**time.Time)(ptr))
return t == nil || (*t).Equal(zeroTime)
}
func (tpc timePointerCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
t := *((**time.Time)(ptr))
if t == nil || (*t).Equal(zeroTime) {
stream.WriteNil()
return
}
stream.WriteString(t.In(time.Local).Format("2006-01-02 15:04:05.000"))
}
func (tpc timePointerCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
ts := iter.ReadString()
if len(ts) == 0 {
*((**time.Time)(ptr)) = nil
return
}
t, err := time.ParseInLocation("2006-01-02 15:04:05.000", ts, time.Local)
if err != nil {
panic(err)
}
*((**time.Time)(ptr)) = &t
}
// endregion
+1 -1
View File
@@ -116,7 +116,7 @@ func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request
err := mw.Close()
require.NoError(t, err)
req, err := http.NewRequest("POST", "/", &body)
req, err := http.NewRequest(http.MethodPost, "/", &body)
require.NoError(t, err)
req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())
+1 -1
View File
@@ -15,7 +15,7 @@ func (plainBinding) Name() string {
return "plain"
}
func (plainBinding) Bind(req *http.Request, obj interface{}) error {
func (plainBinding) Bind(req *http.Request, obj any) error {
all, err := io.ReadAll(req.Body)
if err != nil {
return err
+1 -1
View File
@@ -34,7 +34,7 @@ func (protobufBinding) BindBody(body []byte, obj any) error {
if err := proto.Unmarshal(body, msg); err != nil {
return err
}
// Here it's same to return validate(obj), but util now we can't add
// Here it's same to return validate(obj), but until now we can't add
// `binding:""` to the struct which automatically generate by gen-proto
return nil
// return validate(obj)
+1 -1
View File
@@ -31,5 +31,5 @@ func decodeToml(r io.Reader, obj any) error {
if err := decoder.Decode(obj); err != nil {
return err
}
return decoder.Decode(obj)
return validate(obj)
}
+7 -7
View File
@@ -158,16 +158,16 @@ type structNoValidationPointer struct {
}
func TestValidateNoValidationPointers(t *testing.T) {
//origin := createNoValidation_values()
//test := createNoValidation_values()
// origin := createNoValidation_values()
// test := createNoValidation_values()
empty := structNoValidationPointer{}
//assert.Nil(t, validate(test))
//assert.Nil(t, validate(&test))
// assert.Nil(t, validate(test))
// assert.Nil(t, validate(&test))
require.NoError(t, validate(empty))
require.NoError(t, validate(&empty))
//assert.Equal(t, origin, test)
// assert.Equal(t, origin, test)
}
type Object map[string]any
@@ -198,7 +198,7 @@ type structModifyValidation struct {
}
func toZero(sl validator.StructLevel) {
var s *structModifyValidation = sl.Top().Interface().(*structModifyValidation)
s := sl.Top().Interface().(*structModifyValidation)
s.Integer = 0
}
@@ -249,5 +249,5 @@ func TestValidatorEngine(t *testing.T) {
// Check that we got back non-nil errs
require.Error(t, errs)
// Check that the error matches expectation
require.Error(t, errs, "", "", "notone")
require.Error(t, errs, "notone")
}
+1
View File
@@ -24,6 +24,7 @@ func (xmlBinding) Bind(req *http.Request, obj any) error {
func (xmlBinding) BindBody(body []byte, obj any) error {
return decodeXML(bytes.NewReader(body), obj)
}
func decodeXML(r io.Reader, obj any) error {
decoder := xml.NewDecoder(r)
if err := decoder.Decode(obj); err != nil {
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"io"
"net/http"
"gopkg.in/yaml.v3"
"github.com/goccy/go-yaml"
)
type yamlBinding struct{}