diff --git a/binding/binding.go b/binding/binding.go
index 520c5109..a14bce1b 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -4,110 +4,70 @@
package binding
-import "net/http"
+import (
+ "fmt"
-// Content-Type MIME of the most common data formats.
-const (
- MIMEJSON = "application/json"
- MIMEHTML = "text/html"
- MIMEXML = "application/xml"
- MIMEXML2 = "text/xml"
- MIMEPlain = "text/plain"
- MIMEPOSTForm = "application/x-www-form-urlencoded"
- MIMEMultipartPOSTForm = "multipart/form-data"
- MIMEPROTOBUF = "application/x-protobuf"
- MIMEMSGPACK = "application/x-msgpack"
- MIMEMSGPACK2 = "application/msgpack"
- MIMEYAML = "application/x-yaml"
+ "github.com/gin-gonic/gin/binding/common"
)
-// Binding describes the interface which needs to be implemented for binding the
-// data present in the request such as JSON request body, query parameters or
-// the form POST.
-type Binding interface {
- Name() string
- Bind(*http.Request, interface{}) error
-}
-
-// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
-// but it reads the body from supplied bytes instead of req.Body.
-type BindingBody interface {
- Binding
- BindBody([]byte, interface{}) error
-}
-
-// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
-// but it read the Params.
-type BindingUri interface {
- Name() string
- BindUri(map[string][]string, interface{}) error
-}
-
-// StructValidator is the minimal interface which needs to be implemented in
-// order for it to be used as the validator engine for ensuring the correctness
-// of the request. Gin provides a default implementation for this using
-// https://github.com/go-playground/validator/tree/v8.18.2.
-type StructValidator interface {
- // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
- // If the received type is not a struct, any validation should be skipped and nil must be returned.
- // If the received type is a struct or pointer to a struct, the validation should be performed.
- // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
- // Otherwise nil must be returned.
- ValidateStruct(interface{}) error
-
- // Engine returns the underlying validator engine which powers the
- // StructValidator implementation.
- Engine() interface{}
-}
-
-// Validator is the default validator which implements the StructValidator
-// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
-// under the hood.
-var Validator StructValidator = &defaultValidator{}
-
// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
- JSON = jsonBinding{}
- XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
- FormMultipart = formMultipartBinding{}
- ProtoBuf = protobufBinding{}
- MsgPack = msgpackBinding{}
- YAML = yamlBinding{}
Uri = uriBinding{}
+ FormMultipart = formMultipartBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
-func Default(method, contentType string) Binding {
+func Default(method, contentType string) common.Binding {
if method == "GET" {
return Form
}
-
switch contentType {
- case MIMEJSON:
- return JSON
- case MIMEXML, MIMEXML2:
- return XML
- case MIMEPROTOBUF:
- return ProtoBuf
- case MIMEMSGPACK, MIMEMSGPACK2:
- return MsgPack
- case MIMEYAML:
- return YAML
- case MIMEMultipartPOSTForm:
+ case common.MIMEMultipartPOSTForm:
return FormMultipart
- default: // case MIMEPOSTForm:
- return Form
+ default:
+ b, ok := common.List[contentType]
+ if !ok {
+ return Form //Default to Form
+ }
+ return b
}
}
-func validate(obj interface{}) error {
- if Validator == nil {
- return nil
- }
- return Validator.ValidateStruct(obj)
+//YAML return the binding for yaml if loaded
+func YAML() common.BindingBody {
+ return retBinding(common.MIMEYAML)
+}
+
+//JSON return the binding for json if loaded
+func JSON() common.BindingBody {
+ return retBinding(common.MIMEJSON)
+}
+
+//XML return the binding for xml if loaded
+func XML() common.BindingBody {
+ return retBinding(common.MIMEXML)
+}
+
+//ProtoBuf return the binding for ProtoBuf if loaded
+func ProtoBuf() common.BindingBody {
+ return retBinding(common.MIMEPROTOBUF)
+}
+
+//MsgPack return the binding for MsgPack if loaded
+func MsgPack() common.BindingBody {
+ return retBinding(common.MIMEMSGPACK)
+}
+
+//retBinding Search for a render and panic on not found
+func retBinding(contentType string) common.BindingBody {
+ b, ok := common.List[contentType]
+ if !ok {
+ panic(fmt.Sprintf("Undefined binding %s", contentType))
+ }
+ return b
}
diff --git a/binding/binding_body_test.go b/binding/binding_body_test.go
index 901d429c..4e560bfc 100644
--- a/binding/binding_body_test.go
+++ b/binding/binding_body_test.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"testing"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/gin-gonic/gin/testdata/protoexample"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
@@ -14,18 +15,18 @@ import (
func TestBindingBody(t *testing.T) {
for _, tt := range []struct {
name string
- binding BindingBody
+ binding common.BindingBody
body string
want string
}{
{
name: "JSON binding",
- binding: JSON,
+ binding: JSON(),
body: `{"foo":"FOO"}`,
},
{
name: "XML binding",
- binding: XML,
+ binding: XML(),
body: `
FOO
@@ -33,12 +34,12 @@ func TestBindingBody(t *testing.T) {
},
{
name: "MsgPack binding",
- binding: MsgPack,
+ binding: MsgPack(),
body: msgPackBody(t),
},
{
name: "YAML binding",
- binding: YAML,
+ binding: YAML(),
body: `foo: FOO`,
},
} {
@@ -67,6 +68,6 @@ func TestBindingBodyProto(t *testing.T) {
req := requestWithBody("POST", "/", string(data))
form := protoexample.Test{}
body, _ := ioutil.ReadAll(req.Body)
- assert.NoError(t, ProtoBuf.BindBody(body, &form))
+ assert.NoError(t, ProtoBuf().BindBody(body, &form))
assert.Equal(t, test, form)
}
diff --git a/binding/binding_test.go b/binding/binding_test.go
index ee788225..bc502499 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -18,10 +18,17 @@ import (
"testing"
"time"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/gin-gonic/gin/testdata/protoexample"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/ugorji/go/codec"
+
+ _ "github.com/gin-gonic/gin/binding/json"
+ _ "github.com/gin-gonic/gin/binding/msgpack"
+ _ "github.com/gin-gonic/gin/binding/protobuf"
+ _ "github.com/gin-gonic/gin/binding/xml"
+ _ "github.com/gin-gonic/gin/binding/yaml"
)
type FooStruct struct {
@@ -190,54 +197,54 @@ 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("GET", common.MIMEJSON))
- assert.Equal(t, JSON, Default("POST", MIMEJSON))
- assert.Equal(t, JSON, Default("PUT", MIMEJSON))
+ assert.Equal(t, JSON(), Default("POST", common.MIMEJSON))
+ assert.Equal(t, JSON(), Default("PUT", common.MIMEJSON))
- assert.Equal(t, XML, Default("POST", MIMEXML))
- assert.Equal(t, XML, Default("PUT", MIMEXML2))
+ assert.Equal(t, XML(), Default("POST", common.MIMEXML))
+ assert.Equal(t, XML(), Default("PUT", common.MIMEXML2))
- assert.Equal(t, Form, Default("POST", MIMEPOSTForm))
- assert.Equal(t, Form, Default("PUT", MIMEPOSTForm))
+ assert.Equal(t, Form, Default("POST", common.MIMEPOSTForm))
+ assert.Equal(t, Form, Default("PUT", common.MIMEPOSTForm))
- assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm))
- assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm))
+ assert.Equal(t, FormMultipart, Default("POST", common.MIMEMultipartPOSTForm))
+ assert.Equal(t, FormMultipart, Default("PUT", common.MIMEMultipartPOSTForm))
- assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF))
- assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF))
+ assert.Equal(t, ProtoBuf(), Default("POST", common.MIMEPROTOBUF))
+ assert.Equal(t, ProtoBuf(), Default("PUT", common.MIMEPROTOBUF))
- assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
- assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
+ assert.Equal(t, MsgPack(), Default("POST", common.MIMEMSGPACK))
+ assert.Equal(t, MsgPack(), Default("PUT", common.MIMEMSGPACK2))
- assert.Equal(t, YAML, Default("POST", MIMEYAML))
- assert.Equal(t, YAML, Default("PUT", MIMEYAML))
+ assert.Equal(t, YAML(), Default("POST", common.MIMEYAML))
+ assert.Equal(t, YAML(), Default("PUT", common.MIMEYAML))
}
func TestBindingJSONNilBody(t *testing.T) {
var obj FooStruct
req, _ := http.NewRequest(http.MethodPost, "/", nil)
- err := JSON.Bind(req, &obj)
+ err := JSON().Bind(req, &obj)
assert.Error(t, err)
}
func TestBindingJSON(t *testing.T) {
testBodyBinding(t,
- JSON, "json",
+ JSON(), "json",
"/", "/",
`{"foo": "bar"}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber(t *testing.T) {
testBodyBindingUseNumber(t,
- JSON, "json",
+ JSON(), "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
func TestBindingJSONUseNumber2(t *testing.T) {
testBodyBindingUseNumber2(t,
- JSON, "json",
+ JSON(), "json",
"/", "/",
`{"foo": 123}`, `{"bar": "foo"}`)
}
@@ -496,28 +503,28 @@ func TestBindingQueryBoolFail(t *testing.T) {
func TestBindingXML(t *testing.T) {
testBodyBinding(t,
- XML, "xml",
+ XML(), "xml",
"/", "/",
"", "")
}
func TestBindingXMLFail(t *testing.T) {
testBodyBindingFail(t,
- XML, "xml",
+ XML(), "xml",
"/", "/",
"", "")
}
func TestBindingYAML(t *testing.T) {
testBodyBinding(t,
- YAML, "yaml",
+ YAML(), "yaml",
"/", "/",
`foo: bar`, `bar: foo`)
}
func TestBindingYAMLFail(t *testing.T) {
testBodyBindingFail(t,
- YAML, "yaml",
+ YAML(), "yaml",
"/", "/",
`foo:\nbar`, `bar: foo`)
}
@@ -525,28 +532,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"))
assert.NoError(t, err)
- req.Header.Set("Content-Type", MIMEPOSTForm)
+ req.Header.Set("Content-Type", common.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)
+ req.Header.Set("Content-Type", common.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)
+ req.Header.Set("Content-Type", common.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)
+ req.Header.Set("Content-Type", common.MIMEPOSTForm)
return req
}
@@ -569,7 +576,7 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request {
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err2)
- req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -593,7 +600,7 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request {
req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
assert.NoError(t, err2)
- req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -609,7 +616,7 @@ func createFormMultipartRequest(t *testing.T) *http.Request {
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)
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -623,7 +630,7 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request {
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)
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -637,7 +644,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
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)
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -737,7 +744,7 @@ func TestBindingProtoBuf(t *testing.T) {
data, _ := proto.Marshal(test)
testProtoBodyBinding(t,
- ProtoBuf, "protobuf",
+ ProtoBuf(), "protobuf",
"/", "/",
string(data), string(data[1:]))
}
@@ -749,7 +756,7 @@ func TestBindingProtoBufFail(t *testing.T) {
data, _ := proto.Marshal(test)
testProtoBodyBindingFail(t,
- ProtoBuf, "protobuf",
+ ProtoBuf(), "protobuf",
"/", "/",
string(data), string(data[1:]))
}
@@ -769,7 +776,7 @@ func TestBindingMsgPack(t *testing.T) {
data := buf.Bytes()
testMsgPackBodyBinding(t,
- MsgPack, "msgpack",
+ MsgPack(), "msgpack",
"/", "/",
string(data), string(data[1:]))
}
@@ -777,18 +784,18 @@ func TestBindingMsgPack(t *testing.T) {
func TestValidationFails(t *testing.T) {
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
- err := JSON.Bind(req, &obj)
+ err := JSON().Bind(req, &obj)
assert.Error(t, err)
}
func TestValidationDisabled(t *testing.T) {
- backup := Validator
- Validator = nil
- defer func() { Validator = backup }()
+ backup := common.Validator
+ common.Validator = nil
+ defer func() { common.Validator = backup }()
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
- err := JSON.Bind(req, &obj)
+ err := JSON().Bind(req, &obj)
assert.NoError(t, err)
}
@@ -799,7 +806,7 @@ func TestExistsSucceeds(t *testing.T) {
var obj HogeStruct
req := requestWithBody("POST", "/", `{"hoge": 0}`)
- err := JSON.Bind(req, &obj)
+ err := JSON().Bind(req, &obj)
assert.NoError(t, err)
}
@@ -810,7 +817,7 @@ func TestExistsFails(t *testing.T) {
var obj HogeStruct
req := requestWithBody("POST", "/", `{"boen": 0}`)
- err := JSON.Bind(req, &obj)
+ err := JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -864,7 +871,7 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
@@ -873,7 +880,7 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
obj = FooBarStruct{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -884,7 +891,7 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB
obj := FooDefaultBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
@@ -893,7 +900,7 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB
obj = FooDefaultBarStruct{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -911,7 +918,7 @@ 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")
+ req.Header.Set("Content-Type", common.MIMEMultipartPOSTForm+";boundary=testboundary")
_, err = req.MultipartReader()
assert.NoError(t, err)
err = Form.Bind(req, &obj)
@@ -945,7 +952,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
obj := FooBarStructForTimeType{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
@@ -957,7 +964,7 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForTimeType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -968,14 +975,14 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeNotFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeNotFormat{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -986,14 +993,14 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body,
obj := FooStructForTimeTypeFailFormat{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailFormat{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1004,14 +1011,14 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod
obj := FooStructForTimeTypeFailLocation{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = FooStructForTimeTypeFailLocation{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1022,7 +1029,7 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo
obj := FooStructForIgnoreFormTag{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
@@ -1037,7 +1044,7 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
obj := InvalidNameType{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
@@ -1045,7 +1052,7 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
obj = InvalidNameType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1056,14 +1063,14 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB
obj := InvalidNameMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
obj = InvalidNameMapType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1073,7 +1080,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
switch typ {
case "Int":
@@ -1085,7 +1092,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForIntType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Int8":
obj := FooBarStructForInt8Type{}
@@ -1096,7 +1103,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForInt8Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Int16":
obj := FooBarStructForInt16Type{}
@@ -1107,7 +1114,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForInt16Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Int32":
obj := FooBarStructForInt32Type{}
@@ -1118,7 +1125,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForInt32Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Int64":
obj := FooBarStructForInt64Type{}
@@ -1129,7 +1136,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForInt64Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Uint":
obj := FooBarStructForUintType{}
@@ -1140,7 +1147,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForUintType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Uint8":
obj := FooBarStructForUint8Type{}
@@ -1151,7 +1158,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForUint8Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Uint16":
obj := FooBarStructForUint16Type{}
@@ -1162,7 +1169,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForUint16Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Uint32":
obj := FooBarStructForUint32Type{}
@@ -1173,7 +1180,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForUint32Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Uint64":
obj := FooBarStructForUint64Type{}
@@ -1184,7 +1191,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForUint64Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Float32":
obj := FooBarStructForFloat32Type{}
@@ -1195,7 +1202,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForFloat32Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Float64":
obj := FooBarStructForFloat64Type{}
@@ -1206,7 +1213,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForFloat64Type{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Bool":
obj := FooBarStructForBoolType{}
@@ -1217,7 +1224,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooBarStructForBoolType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Slice":
obj := FooStructForSliceType{}
@@ -1227,7 +1234,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
obj = FooStructForSliceType{}
req = requestWithBody(method, badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
case "Struct":
obj := FooStructForStructType{}
@@ -1291,7 +1298,7 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
obj := FooBarStruct{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.NoError(t, err)
@@ -1306,7 +1313,7 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str
obj := FooStructForMapType{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
@@ -1319,13 +1326,13 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody
obj := FooStructForBoolType{}
req := requestWithBody(method, path, body)
if method == "POST" {
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
}
err := b.Bind(req, &obj)
assert.Error(t, err)
}
-func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testBodyBinding(t *testing.T, b common.Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStruct{}
@@ -1336,16 +1343,16 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
-func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testBodyBindingUseNumber(t *testing.T, b common.Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
- EnableDecoderUseNumber = true
+ common.EnableDecoderUseNumber = true
err := b.Bind(req, &obj)
assert.NoError(t, err)
// we hope it is int64(123)
@@ -1355,16 +1362,16 @@ func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
-func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testBodyBindingUseNumber2(t *testing.T, b common.Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStructUseNumber{}
req := requestWithBody("POST", path, body)
- EnableDecoderUseNumber = false
+ common.EnableDecoderUseNumber = false
err := b.Bind(req, &obj)
assert.NoError(t, err)
// it will return float64(123) if not use EnableDecoderUseNumber
@@ -1373,11 +1380,11 @@ func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, bod
obj = FooStructUseNumber{}
req = requestWithBody("POST", badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
-func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testBodyBindingFail(t *testing.T, b common.Binding, name, path, badPath, body, badBody string) {
assert.Equal(t, name, b.Name())
obj := FooStruct{}
@@ -1388,24 +1395,24 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad
obj = FooStruct{}
req = requestWithBody("POST", badPath, badBody)
- err = JSON.Bind(req, &obj)
+ err = JSON().Bind(req, &obj)
assert.Error(t, err)
}
-func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testProtoBodyBinding(t *testing.T, b common.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)
+ req.Header.Add("Content-Type", common.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)
+ req.Header.Add("Content-Type", common.MIMEPROTOBUF)
+ err = ProtoBuf().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1415,38 +1422,38 @@ 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) {
+func testProtoBodyBindingFail(t *testing.T, b common.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)
+ req.Header.Add("Content-Type", common.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)
+ req.Header.Add("Content-Type", common.MIMEPROTOBUF)
+ err = ProtoBuf().Bind(req, &obj)
assert.Error(t, err)
}
-func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
+func testMsgPackBodyBinding(t *testing.T, b common.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)
+ req.Header.Add("Content-Type", common.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)
+ req.Header.Add("Content-Type", common.MIMEMSGPACK)
+ err = MsgPack().Bind(req, &obj)
assert.Error(t, err)
}
@@ -1466,7 +1473,7 @@ func TestCanSet(t *testing.T) {
func formPostRequest(path, body string) *http.Request {
req := requestWithBody("POST", path, body)
- req.Header.Add("Content-Type", MIMEPOSTForm)
+ req.Header.Add("Content-Type", common.MIMEPOSTForm)
return req
}
@@ -1548,3 +1555,14 @@ func TestBindingArray(t *testing.T) {
err = Form.Bind(req, &s)
assert.Error(t, err)
}
+
+func TestBindingPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("retBinding did not panic")
+ }
+ }()
+
+ //Should panic
+ retBinding("NotKnowBinding")
+}
diff --git a/binding/common/common.go b/binding/common/common.go
new file mode 100644
index 00000000..c7d984a6
--- /dev/null
+++ b/binding/common/common.go
@@ -0,0 +1,78 @@
+package common
+
+import "net/http"
+
+// Content-Type MIME of the most common data formats.
+const (
+ MIMEJSON = "application/json"
+ MIMEHTML = "text/html"
+ MIMEXML = "application/xml"
+ MIMEXML2 = "text/xml"
+ MIMEPlain = "text/plain"
+ MIMEPOSTForm = "application/x-www-form-urlencoded"
+ MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/x-protobuf"
+ MIMEMSGPACK = "application/x-msgpack"
+ MIMEMSGPACK2 = "application/msgpack"
+ MIMEYAML = "application/x-yaml"
+)
+
+//List hold the defined binder
+var List = map[string]BindingBody{}
+
+// Binding describes the interface which needs to be implemented for binding the
+// data present in the request such as JSON request body, query parameters or
+// the form POST.
+type Binding interface {
+ Name() string
+ Bind(*http.Request, interface{}) error
+}
+
+// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
+// but it reads the body from supplied bytes instead of req.Body.
+type BindingBody interface {
+ Binding
+ BindBody([]byte, interface{}) error
+}
+
+// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
+// but it read the Params.
+type BindingUri interface {
+ Name() string
+ BindUri(map[string][]string, interface{}) error
+}
+
+// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
+// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
+// interface{} as a Number instead of as a float64.
+var EnableDecoderUseNumber = false
+
+// StructValidator is the minimal interface which needs to be implemented in
+// order for it to be used as the validator engine for ensuring the correctness
+// of the request. Gin provides a default implementation for this using
+// https://github.com/go-playground/validator/tree/v8.18.2.
+type StructValidator interface {
+ // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
+ // If the received type is not a struct, any validation should be skipped and nil must be returned.
+ // If the received type is a struct or pointer to a struct, the validation should be performed.
+ // If the struct is not valid or the validation itself fails, a descriptive error should be returned.
+ // Otherwise nil must be returned.
+ ValidateStruct(interface{}) error
+
+ // Engine returns the underlying validator engine which powers the
+ // StructValidator implementation.
+ Engine() interface{}
+}
+
+// Validator is the default validator which implements the StructValidator
+// interface. It uses https://github.com/go-playground/validator/tree/v8.18.2
+// under the hood.
+var Validator StructValidator = &defaultValidator{}
+
+//Validate standard validate object
+func Validate(obj interface{}) error {
+ if Validator == nil {
+ return nil
+ }
+ return Validator.ValidateStruct(obj)
+}
diff --git a/binding/default_validator.go b/binding/common/default_validator.go
similarity index 98%
rename from binding/default_validator.go
rename to binding/common/default_validator.go
index e7a302de..7d3b0a2e 100644
--- a/binding/default_validator.go
+++ b/binding/common/default_validator.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package common
import (
"reflect"
diff --git a/binding/validate_test.go b/binding/common/validate_test.go
similarity index 90%
rename from binding/validate_test.go
rename to binding/common/validate_test.go
index 2c76b6d6..7d608ee1 100644
--- a/binding/validate_test.go
+++ b/binding/common/validate_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package common
import (
"bytes"
@@ -114,10 +114,10 @@ func TestValidateNoValidationValues(t *testing.T) {
test := createNoValidationValues()
empty := structNoValidationValues{}
- assert.Nil(t, validate(test))
- assert.Nil(t, validate(&test))
- assert.Nil(t, validate(empty))
- assert.Nil(t, validate(&empty))
+ assert.Nil(t, Validate(test))
+ assert.Nil(t, Validate(&test))
+ assert.Nil(t, Validate(empty))
+ assert.Nil(t, Validate(&empty))
assert.Equal(t, origin, test)
}
@@ -162,10 +162,10 @@ func TestValidateNoValidationPointers(t *testing.T) {
//test := createNoValidation_values()
empty := structNoValidationPointer{}
- //assert.Nil(t, validate(test))
- //assert.Nil(t, validate(&test))
- assert.Nil(t, validate(empty))
- assert.Nil(t, validate(&empty))
+ //assert.Nil(t, Validate(test))
+ //assert.Nil(t, Validate(&test))
+ assert.Nil(t, Validate(empty))
+ assert.Nil(t, Validate(&empty))
//assert.Equal(t, origin, test)
}
@@ -174,22 +174,22 @@ type Object map[string]interface{}
func TestValidatePrimitives(t *testing.T) {
obj := Object{"foo": "bar", "bar": 1}
- assert.NoError(t, validate(obj))
- assert.NoError(t, validate(&obj))
+ assert.NoError(t, Validate(obj))
+ assert.NoError(t, Validate(&obj))
assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
- assert.NoError(t, validate(obj2))
- assert.NoError(t, validate(&obj2))
+ assert.NoError(t, Validate(obj2))
+ assert.NoError(t, Validate(&obj2))
nu := 10
- assert.NoError(t, validate(nu))
- assert.NoError(t, validate(&nu))
+ assert.NoError(t, Validate(nu))
+ assert.NoError(t, Validate(&nu))
assert.Equal(t, 10, nu)
str := "value"
- assert.NoError(t, validate(str))
- assert.NoError(t, validate(&str))
+ assert.NoError(t, Validate(str))
+ assert.NoError(t, Validate(&str))
assert.Equal(t, "value", str)
}
@@ -227,7 +227,7 @@ func TestValidatorEngine(t *testing.T) {
// Create an instance which will fail validation
withOne := structCustomValidation{Integer: 1}
- errs := validate(withOne)
+ errs := Validate(withOne)
// Check that we got back non-nil errs
assert.NotNil(t, errs)
diff --git a/binding/form.go b/binding/form.go
index 0b28aa8a..c9f675ee 100644
--- a/binding/form.go
+++ b/binding/form.go
@@ -8,6 +8,8 @@ import (
"mime/multipart"
"net/http"
"reflect"
+
+ "github.com/gin-gonic/gin/binding/common"
)
const defaultMemory = 32 * 1024 * 1024
@@ -32,7 +34,7 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error {
if err := mapForm(obj, req.Form); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
func (formPostBinding) Name() string {
@@ -46,7 +48,7 @@ func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
if err := mapForm(obj, req.PostForm); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
func (formMultipartBinding) Name() string {
@@ -61,7 +63,7 @@ func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
type multipartRequest http.Request
diff --git a/binding/json.go b/binding/json/json.go
similarity index 72%
rename from binding/json.go
rename to binding/json/json.go
index f968161b..6e7441cc 100644
--- a/binding/json.go
+++ b/binding/json/json.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package json
import (
"bytes"
@@ -10,13 +10,13 @@ import (
"io"
"net/http"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/gin-gonic/gin/internal/json"
)
-// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
-// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
-// interface{} as a Number instead of as a float64.
-var EnableDecoderUseNumber = false
+func init() {
+ common.List[common.MIMEJSON] = jsonBinding{}
+}
type jsonBinding struct{}
@@ -37,11 +37,11 @@ func (jsonBinding) BindBody(body []byte, obj interface{}) error {
func decodeJSON(r io.Reader, obj interface{}) error {
decoder := json.NewDecoder(r)
- if EnableDecoderUseNumber {
+ if common.EnableDecoderUseNumber {
decoder.UseNumber()
}
if err := decoder.Decode(obj); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/binding/msgpack.go b/binding/msgpack/msgpack.go
similarity index 76%
rename from binding/msgpack.go
rename to binding/msgpack/msgpack.go
index b7f73197..c8bea55c 100644
--- a/binding/msgpack.go
+++ b/binding/msgpack/msgpack.go
@@ -2,16 +2,23 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package msgpack
import (
"bytes"
"io"
"net/http"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/ugorji/go/codec"
)
+func init() {
+ msgPack := msgpackBinding{}
+ common.List[common.MIMEMSGPACK] = msgPack
+ common.List[common.MIMEMSGPACK2] = msgPack
+}
+
type msgpackBinding struct{}
func (msgpackBinding) Name() string {
@@ -31,5 +38,5 @@ func decodeMsgPack(r io.Reader, obj interface{}) error {
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/binding/protobuf.go b/binding/protobuf/protobuf.go
similarity index 86%
rename from binding/protobuf.go
rename to binding/protobuf/protobuf.go
index f9ece928..5162aeeb 100644
--- a/binding/protobuf.go
+++ b/binding/protobuf/protobuf.go
@@ -2,15 +2,20 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package protobuf
import (
"io/ioutil"
"net/http"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/golang/protobuf/proto"
)
+func init() {
+ common.List[common.MIMEPROTOBUF] = protobufBinding{}
+}
+
type protobufBinding struct{}
func (protobufBinding) Name() string {
diff --git a/binding/query.go b/binding/query.go
index 219743f2..23da5550 100644
--- a/binding/query.go
+++ b/binding/query.go
@@ -4,7 +4,11 @@
package binding
-import "net/http"
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin/binding/common"
+)
type queryBinding struct{}
@@ -17,5 +21,5 @@ func (queryBinding) Bind(req *http.Request, obj interface{}) error {
if err := mapForm(obj, values); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/binding/uri.go b/binding/uri.go
index f91ec381..cd6e4448 100644
--- a/binding/uri.go
+++ b/binding/uri.go
@@ -4,6 +4,8 @@
package binding
+import "github.com/gin-gonic/gin/binding/common"
+
type uriBinding struct{}
func (uriBinding) Name() string {
@@ -14,5 +16,5 @@ func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
if err := mapUri(obj, m); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/binding/xml.go b/binding/xml/xml.go
similarity index 77%
rename from binding/xml.go
rename to binding/xml/xml.go
index 4e901149..16a25636 100644
--- a/binding/xml.go
+++ b/binding/xml/xml.go
@@ -2,15 +2,23 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package xml
import (
"bytes"
"encoding/xml"
"io"
"net/http"
+
+ "github.com/gin-gonic/gin/binding/common"
)
+func init() {
+ xml := xmlBinding{}
+ common.List[common.MIMEXML] = xml
+ common.List[common.MIMEXML2] = xml
+}
+
type xmlBinding struct{}
func (xmlBinding) Name() string {
@@ -29,5 +37,5 @@ func decodeXML(r io.Reader, obj interface{}) error {
if err := decoder.Decode(obj); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/binding/yaml.go b/binding/yaml/yaml.go
similarity index 81%
rename from binding/yaml.go
rename to binding/yaml/yaml.go
index a2d36d6a..03b67545 100644
--- a/binding/yaml.go
+++ b/binding/yaml/yaml.go
@@ -2,16 +2,21 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package binding
+package yaml
import (
"bytes"
"io"
"net/http"
+ "github.com/gin-gonic/gin/binding/common"
"gopkg.in/yaml.v2"
)
+func init() {
+ common.List[common.MIMEYAML] = yamlBinding{}
+}
+
type yamlBinding struct{}
func (yamlBinding) Name() string {
@@ -31,5 +36,5 @@ func decodeYAML(r io.Reader, obj interface{}) error {
if err := decoder.Decode(obj); err != nil {
return err
}
- return validate(obj)
+ return common.Validate(obj)
}
diff --git a/context.go b/context.go
index 5dc7f8a0..6c301d81 100644
--- a/context.go
+++ b/context.go
@@ -18,22 +18,16 @@ import (
"strings"
"time"
+ commonB "github.com/gin-gonic/gin/binding/common"
+ "github.com/gin-gonic/gin/render/common"
+
"github.com/gin-contrib/sse"
"github.com/gin-gonic/gin/binding"
"github.com/gin-gonic/gin/render"
)
-// Content-Type MIME of the most common data formats.
const (
- MIMEJSON = binding.MIMEJSON
- MIMEHTML = binding.MIMEHTML
- MIMEXML = binding.MIMEXML
- MIMEXML2 = binding.MIMEXML2
- MIMEPlain = binding.MIMEPlain
- MIMEPOSTForm = binding.MIMEPOSTForm
- MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
- MIMEYAML = binding.MIMEYAML
- BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
+ BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
)
const abortIndex int8 = math.MaxInt8 / 2
@@ -536,12 +530,12 @@ func (c *Context) Bind(obj interface{}) error {
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func (c *Context) BindJSON(obj interface{}) error {
- return c.MustBindWith(obj, binding.JSON)
+ return c.MustBindWith(obj, binding.JSON())
}
// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
func (c *Context) BindXML(obj interface{}) error {
- return c.MustBindWith(obj, binding.XML)
+ return c.MustBindWith(obj, binding.XML())
}
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
@@ -551,7 +545,7 @@ func (c *Context) BindQuery(obj interface{}) error {
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
func (c *Context) BindYAML(obj interface{}) error {
- return c.MustBindWith(obj, binding.YAML)
+ return c.MustBindWith(obj, binding.YAML())
}
// BindUri binds the passed struct pointer using binding.Uri.
@@ -567,7 +561,7 @@ func (c *Context) BindUri(obj interface{}) error {
// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
-func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) MustBindWith(obj interface{}, b commonB.Binding) error {
if err := c.ShouldBindWith(obj, b); err != nil {
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
return err
@@ -590,12 +584,12 @@ func (c *Context) ShouldBind(obj interface{}) error {
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (c *Context) ShouldBindJSON(obj interface{}) error {
- return c.ShouldBindWith(obj, binding.JSON)
+ return c.ShouldBindWith(obj, binding.JSON())
}
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
func (c *Context) ShouldBindXML(obj interface{}) error {
- return c.ShouldBindWith(obj, binding.XML)
+ return c.ShouldBindWith(obj, binding.XML())
}
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
@@ -605,7 +599,7 @@ func (c *Context) ShouldBindQuery(obj interface{}) error {
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
func (c *Context) ShouldBindYAML(obj interface{}) error {
- return c.ShouldBindWith(obj, binding.YAML)
+ return c.ShouldBindWith(obj, binding.YAML())
}
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
@@ -619,7 +613,7 @@ func (c *Context) ShouldBindUri(obj interface{}) error {
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
// See the binding package.
-func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) ShouldBindWith(obj interface{}, b commonB.Binding) error {
return b.Bind(c.Request, obj)
}
@@ -628,7 +622,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
//
// NOTE: This method reads the body before binding. So you should use
// ShouldBindWith for better performance if you need to call only once.
-func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
+func (c *Context) ShouldBindBodyWith(obj interface{}, bb commonB.BindingBody) (err error) {
var body []byte
if cb, ok := c.Get(BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
@@ -767,7 +761,7 @@ func (c *Context) Cookie(name string) (string, error) {
}
// Render writes the response headers and calls render.Render to render data.
-func (c *Context) Render(code int, r render.Render) {
+func (c *Context) Render(code int, r common.Render) {
c.Status(code)
if !bodyAllowedForStatus(code) {
@@ -794,14 +788,14 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj interface{}) {
- c.Render(code, render.IndentedJSON{Data: obj})
+ c.Render(code, render.IndentedJSON(obj))
}
// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj interface{}) {
- c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj})
+ c.Render(code, render.SecureJSON(c.engine.secureJsonPrefix, obj))
}
// JSONP serializes the given struct as JSON into the response body.
@@ -810,38 +804,43 @@ func (c *Context) SecureJSON(code int, obj interface{}) {
func (c *Context) JSONP(code int, obj interface{}) {
callback := c.DefaultQuery("callback", "")
if callback == "" {
- c.Render(code, render.JSON{Data: obj})
+ c.Render(code, render.JSON(obj))
return
}
- c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
+ c.Render(code, render.JsonpJSON(callback, obj))
}
// JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj interface{}) {
- c.Render(code, render.JSON{Data: obj})
+ c.Render(code, render.JSON(obj))
}
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
func (c *Context) AsciiJSON(code int, obj interface{}) {
- c.Render(code, render.AsciiJSON{Data: obj})
+ c.Render(code, render.AsciiJSON(obj))
}
// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) {
- c.Render(code, render.XML{Data: obj})
+ c.Render(code, render.XML(obj))
}
// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj interface{}) {
- c.Render(code, render.YAML{Data: obj})
+ c.Render(code, render.YAML(obj))
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj interface{}) {
- c.Render(code, render.ProtoBuf{Data: obj})
+ c.Render(code, render.ProtoBuf(obj))
+}
+
+// MsgPack serializes the given struct as MsgPack into the response body.
+func (c *Context) MsgPack(code int, obj interface{}) {
+ c.Render(code, render.MsgPack(obj))
}
// String writes the given string into the response body.
@@ -932,15 +931,15 @@ type Negotiate struct {
// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
- case binding.MIMEJSON:
+ case commonB.MIMEJSON:
data := chooseData(config.JSONData, config.Data)
c.JSON(code, data)
- case binding.MIMEHTML:
+ case commonB.MIMEHTML:
data := chooseData(config.HTMLData, config.Data)
c.HTML(code, config.HTMLName, data)
- case binding.MIMEXML:
+ case commonB.MIMEXML:
data := chooseData(config.XMLData, config.Data)
c.XML(code, data)
diff --git a/context_17.go b/context_17.go
index 8e9f75ad..6eb1fb0a 100644
--- a/context_17.go
+++ b/context_17.go
@@ -13,5 +13,5 @@ import (
// PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (c *Context) PureJSON(code int, obj interface{}) {
- c.Render(code, render.PureJSON{Data: obj})
+ c.Render(code, render.PureJSON(obj))
}
diff --git a/context_test.go b/context_test.go
index 0da5fbe6..a1b10615 100644
--- a/context_test.go
+++ b/context_test.go
@@ -20,11 +20,23 @@ import (
"github.com/gin-contrib/sse"
"github.com/gin-gonic/gin/binding"
+ commonB "github.com/gin-gonic/gin/binding/common"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
+ "github.com/ugorji/go/codec"
"golang.org/x/net/context"
testdata "github.com/gin-gonic/gin/testdata/protoexample"
+
+ _ "github.com/gin-gonic/gin/binding/json"
+ _ "github.com/gin-gonic/gin/binding/xml"
+ _ "github.com/gin-gonic/gin/binding/yaml"
+
+ _ "github.com/gin-gonic/gin/render/json"
+ _ "github.com/gin-gonic/gin/render/msgpack"
+ _ "github.com/gin-gonic/gin/render/protobuf"
+ _ "github.com/gin-gonic/gin/render/xml"
+ _ "github.com/gin-gonic/gin/render/yaml"
)
var _ context.Context = &Context{}
@@ -55,7 +67,7 @@ func createMultipartRequest() *http.Request {
must(mw.WriteField("names[b]", "tianou"))
req, err := http.NewRequest("POST", "/", body)
must(err)
- req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
+ req.Header.Set("Content-Type", commonB.MIMEMultipartPOSTForm+"; boundary="+boundary)
return req
}
@@ -413,7 +425,7 @@ func TestContextQueryAndPostForm(t *testing.T) {
body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
c.Request, _ = http.NewRequest("POST",
"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
- c.Request.Header.Add("Content-Type", MIMEPOSTForm)
+ c.Request.Header.Add("Content-Type", commonB.MIMEPOSTForm)
assert.Equal(t, "bar", c.DefaultPostForm("foo", "none"))
assert.Equal(t, "bar", c.PostForm("foo"))
@@ -1005,6 +1017,32 @@ func TestContextRenderYAML(t *testing.T) {
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
}
+// TestContextRenderMsgPack tests that the response is serialized as MsgPack
+// and Content-Type is set to application/msgpack; charset=utf-8
+// and we just use the example MsgPack to check if the response is correct
+func TestContextRenderMsgPack(t *testing.T) {
+ w := httptest.NewRecorder()
+ c, _ := CreateTestContext(w)
+
+ reps := []int64{int64(1), int64(2)}
+ label := "test"
+ data := &testdata.Test{
+ Label: &label,
+ Reps: reps,
+ }
+
+ c.MsgPack(http.StatusCreated, data)
+
+ var mh codec.MsgpackHandle
+ var msgData bytes.Buffer
+ err := codec.NewEncoder(&msgData, &mh).Encode(data)
+ assert.NoError(t, err)
+
+ assert.Equal(t, http.StatusCreated, w.Code)
+ assert.Equal(t, msgData.String(), w.Body.String())
+ assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
+}
+
// TestContextRenderProtoBuf tests that the response is serialized as ProtoBuf
// and Content-Type is set to application/x-protobuf
// and we just use the example protobuf to check if the response is correct
@@ -1103,7 +1141,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
- Offered: []string{MIMEJSON, MIMEXML},
+ Offered: []string{commonB.MIMEJSON, commonB.MIMEXML},
Data: H{"foo": "bar"},
})
@@ -1118,7 +1156,7 @@ func TestContextNegotiationWithXML(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
- Offered: []string{MIMEXML, MIMEJSON},
+ Offered: []string{commonB.MIMEXML, commonB.MIMEJSON},
Data: H{"foo": "bar"},
})
@@ -1135,7 +1173,7 @@ func TestContextNegotiationWithHTML(t *testing.T) {
router.SetHTMLTemplate(templ)
c.Negotiate(http.StatusOK, Negotiate{
- Offered: []string{MIMEHTML},
+ Offered: []string{commonB.MIMEHTML},
Data: H{"name": "gin"},
HTMLName: "t",
})
@@ -1151,7 +1189,7 @@ func TestContextNegotiationNotSupport(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
- Offered: []string{MIMEPOSTForm},
+ Offered: []string{commonB.MIMEPOSTForm},
})
assert.Equal(t, http.StatusNotAcceptable, w.Code)
@@ -1164,8 +1202,8 @@ func TestContextNegotiationFormat(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "", nil)
assert.Panics(t, func() { c.NegotiateFormat() })
- assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
- assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML, MIMEJSON))
+ assert.Equal(t, commonB.MIMEJSON, c.NegotiateFormat(commonB.MIMEJSON, commonB.MIMEXML))
+ assert.Equal(t, commonB.MIMEHTML, c.NegotiateFormat(commonB.MIMEHTML, commonB.MIMEJSON))
}
func TestContextNegotiationFormatWithAccept(t *testing.T) {
@@ -1173,9 +1211,9 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) {
c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
- assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
- assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEXML, MIMEHTML))
- assert.Empty(t, c.NegotiateFormat(MIMEJSON))
+ assert.Equal(t, commonB.MIMEXML, c.NegotiateFormat(commonB.MIMEJSON, commonB.MIMEXML))
+ assert.Equal(t, commonB.MIMEHTML, c.NegotiateFormat(commonB.MIMEXML, commonB.MIMEHTML))
+ assert.Empty(t, c.NegotiateFormat(commonB.MIMEJSON))
}
func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
@@ -1186,9 +1224,9 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
assert.Equal(t, c.NegotiateFormat("application/*"), "application/*")
- assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON)
- assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML)
- assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEJSON), commonB.MIMEJSON)
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEXML), commonB.MIMEXML)
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEHTML), commonB.MIMEHTML)
c, _ = CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", nil)
@@ -1197,9 +1235,9 @@ func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
assert.Equal(t, c.NegotiateFormat("application/*"), "")
- assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
- assert.Equal(t, c.NegotiateFormat(MIMEXML), "")
- assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEJSON), "")
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEXML), "")
+ assert.Equal(t, c.NegotiateFormat(commonB.MIMEHTML), commonB.MIMEHTML)
}
func TestContextNegotiationFormatCustom(t *testing.T) {
@@ -1208,11 +1246,11 @@ func TestContextNegotiationFormatCustom(t *testing.T) {
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
c.Accepted = nil
- c.SetAccepted(MIMEJSON, MIMEXML)
+ c.SetAccepted(commonB.MIMEJSON, commonB.MIMEXML)
- assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
- assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML, MIMEHTML))
- assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
+ assert.Equal(t, commonB.MIMEJSON, c.NegotiateFormat(commonB.MIMEJSON, commonB.MIMEXML))
+ assert.Equal(t, commonB.MIMEXML, c.NegotiateFormat(commonB.MIMEXML, commonB.MIMEHTML))
+ assert.Equal(t, commonB.MIMEJSON, c.NegotiateFormat(commonB.MIMEJSON))
}
func TestContextIsAborted(t *testing.T) {
@@ -1376,7 +1414,7 @@ func TestContextContentType(t *testing.T) {
func TestContextAutoBindJSON(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEJSON)
+ c.Request.Header.Add("Content-Type", commonB.MIMEJSON)
var obj struct {
Foo string `json:"foo"`
@@ -1393,7 +1431,7 @@ func TestContextBindWithJSON(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `json:"foo"`
@@ -1413,7 +1451,7 @@ func TestContextBindWithXML(t *testing.T) {
FOO
BAR
`))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `xml:"foo"`
@@ -1446,7 +1484,7 @@ func TestContextBindWithYAML(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `yaml:"foo"`
@@ -1463,7 +1501,7 @@ func TestContextBadAutoBind(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEJSON)
+ c.Request.Header.Add("Content-Type", commonB.MIMEJSON)
var obj struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
@@ -1482,7 +1520,7 @@ func TestContextBadAutoBind(t *testing.T) {
func TestContextAutoShouldBindJSON(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEJSON)
+ c.Request.Header.Add("Content-Type", commonB.MIMEJSON)
var obj struct {
Foo string `json:"foo"`
@@ -1499,7 +1537,7 @@ func TestContextShouldBindWithJSON(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `json:"foo"`
@@ -1520,7 +1558,7 @@ func TestContextShouldBindWithXML(t *testing.T) {
FOO
BAR
`))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `xml:"foo"`
@@ -1557,7 +1595,7 @@ func TestContextShouldBindWithYAML(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
- c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+ c.Request.Header.Add("Content-Type", commonB.MIMEXML) // set fake content-type
var obj struct {
Foo string `yaml:"foo"`
@@ -1574,7 +1612,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
- c.Request.Header.Add("Content-Type", MIMEJSON)
+ c.Request.Header.Add("Content-Type", commonB.MIMEJSON)
var obj struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
@@ -1597,20 +1635,20 @@ func TestContextShouldBindBodyWith(t *testing.T) {
}
for _, tt := range []struct {
name string
- bindingA, bindingB binding.BindingBody
+ bindingA, bindingB commonB.BindingBody
bodyA, bodyB string
}{
{
name: "JSON & JSON",
- bindingA: binding.JSON,
- bindingB: binding.JSON,
+ bindingA: binding.JSON(),
+ bindingB: binding.JSON(),
bodyA: `{"foo":"FOO"}`,
bodyB: `{"bar":"BAR"}`,
},
{
name: "JSON & XML",
- bindingA: binding.JSON,
- bindingB: binding.XML,
+ bindingA: binding.JSON(),
+ bindingB: binding.XML(),
bodyA: `{"foo":"FOO"}`,
bodyB: `
@@ -1619,8 +1657,8 @@ func TestContextShouldBindBodyWith(t *testing.T) {
},
{
name: "XML & XML",
- bindingA: binding.XML,
- bindingB: binding.XML,
+ bindingA: binding.XML(),
+ bindingB: binding.XML(),
bodyA: `
FOO
@@ -1718,7 +1756,7 @@ func TestContextGetRawData(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
body := bytes.NewBufferString("Fetch binary post data")
c.Request, _ = http.NewRequest("POST", "/", body)
- c.Request.Header.Add("Content-Type", MIMEPOSTForm)
+ c.Request.Header.Add("Content-Type", commonB.MIMEPOSTForm)
data, err := c.GetRawData()
assert.Nil(t, err)
diff --git a/deprecated.go b/deprecated.go
index ab447429..e2978d24 100644
--- a/deprecated.go
+++ b/deprecated.go
@@ -7,12 +7,12 @@ package gin
import (
"log"
- "github.com/gin-gonic/gin/binding"
+ "github.com/gin-gonic/gin/binding/common"
)
// BindWith binds the passed struct pointer using the specified binding engine.
// See the binding package.
-func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
+func (c *Context) BindWith(obj interface{}, b common.Binding) error {
log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
be deprecated, please check issue #662 and either use MustBindWith() if you
want HTTP 400 to be automatically returned if any error occur, or use
diff --git a/gin.go b/gin.go
index 2d24092f..2a8fa964 100644
--- a/gin.go
+++ b/gin.go
@@ -13,6 +13,8 @@ import (
"path"
"sync"
+ "github.com/gin-gonic/gin/binding/common"
+
"github.com/gin-gonic/gin/render"
)
@@ -417,7 +419,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
serveError(c, http.StatusNotFound, default404Body)
}
-var mimePlain = []string{MIMEPlain}
+var mimePlain = []string{common.MIMEPlain}
func serveError(c *Context, code int, defaultMessage []byte) {
c.writermem.status = code
diff --git a/mode.go b/mode.go
index 8aa84aa8..af87284a 100644
--- a/mode.go
+++ b/mode.go
@@ -8,7 +8,7 @@ import (
"io"
"os"
- "github.com/gin-gonic/gin/binding"
+ "github.com/gin-gonic/gin/binding/common"
)
// EnvGinMode indicates environment name for gin mode.
@@ -68,13 +68,13 @@ func SetMode(value string) {
// DisableBindValidation closes the default validator.
func DisableBindValidation() {
- binding.Validator = nil
+ common.Validator = nil
}
// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumberto to
// call the UseNumber method on the JSON Decoder instance.
func EnableJsonDecoderUseNumber() {
- binding.EnableDecoderUseNumber = true
+ common.EnableDecoderUseNumber = true
}
// Mode returns currently gin mode.
diff --git a/mode_test.go b/mode_test.go
index 3dba5150..05ab2a3e 100644
--- a/mode_test.go
+++ b/mode_test.go
@@ -8,7 +8,7 @@ import (
"os"
"testing"
- "github.com/gin-gonic/gin/binding"
+ "github.com/gin-gonic/gin/binding/common"
"github.com/stretchr/testify/assert"
)
@@ -41,7 +41,15 @@ func TestSetMode(t *testing.T) {
}
func TestEnableJsonDecoderUseNumber(t *testing.T) {
- assert.False(t, binding.EnableDecoderUseNumber)
+ assert.False(t, common.EnableDecoderUseNumber)
EnableJsonDecoderUseNumber()
- assert.True(t, binding.EnableDecoderUseNumber)
+ assert.True(t, common.EnableDecoderUseNumber)
+}
+
+func TestDisableBindValidation(t *testing.T) {
+ bckp := common.Validator
+ assert.NotNil(t, common.Validator)
+ DisableBindValidation()
+ assert.Nil(t, common.Validator)
+ common.Validator = bckp
}
diff --git a/render/common/common.go b/render/common/common.go
new file mode 100644
index 00000000..cfcc2079
--- /dev/null
+++ b/render/common/common.go
@@ -0,0 +1,24 @@
+package common
+
+import (
+ "net/http"
+)
+
+//WriteContentType set the content-type header
+func WriteContentType(w http.ResponseWriter, value []string) {
+ header := w.Header()
+ if val := header["Content-Type"]; len(val) == 0 {
+ header["Content-Type"] = value
+ }
+}
+
+// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
+type Render interface {
+ // Render writes data with custom ContentType.
+ Render(http.ResponseWriter) error
+ // WriteContentType writes custom ContentType.
+ WriteContentType(w http.ResponseWriter)
+}
+
+//List hold the defined render (obj, options)
+var List = map[string]func(interface{}, map[string]string) Render{}
diff --git a/render/data.go b/render/data.go
index 6ba657ba..8b5d9cf9 100644
--- a/render/data.go
+++ b/render/data.go
@@ -4,7 +4,11 @@
package render
-import "net/http"
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin/render/common"
+)
// Data contains ContentType and bytes data.
type Data struct {
@@ -21,5 +25,5 @@ func (r Data) Render(w http.ResponseWriter) (err error) {
// WriteContentType (Data) writes custom ContentType.
func (r Data) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, []string{r.ContentType})
+ common.WriteContentType(w, []string{r.ContentType})
}
diff --git a/render/html.go b/render/html.go
index 6696ece9..163eff0a 100644
--- a/render/html.go
+++ b/render/html.go
@@ -7,6 +7,8 @@ package render
import (
"html/template"
"net/http"
+
+ "github.com/gin-gonic/gin/render/common"
)
// Delims represents a set of Left and Right delimiters for HTML template rendering.
@@ -20,7 +22,7 @@ type Delims struct {
// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
type HTMLRender interface {
// Instance returns an HTML instance.
- Instance(string, interface{}) Render
+ Instance(string, interface{}) HTML
}
// HTMLProduction contains template reference and its delims.
@@ -47,7 +49,7 @@ type HTML struct {
var htmlContentType = []string{"text/html; charset=utf-8"}
// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
-func (r HTMLProduction) Instance(name string, data interface{}) Render {
+func (r HTMLProduction) Instance(name string, data interface{}) HTML {
return HTML{
Template: r.Template,
Name: name,
@@ -56,7 +58,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
}
// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
-func (r HTMLDebug) Instance(name string, data interface{}) Render {
+func (r HTMLDebug) Instance(name string, data interface{}) HTML {
return HTML{
Template: r.loadTemplate(),
Name: name,
@@ -88,5 +90,5 @@ func (r HTML) Render(w http.ResponseWriter) error {
// WriteContentType (HTML) writes HTML ContentType.
func (r HTML) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, htmlContentType)
+ common.WriteContentType(w, htmlContentType)
}
diff --git a/render/json.go b/render/json/json.go
similarity index 75%
rename from render/json.go
rename to render/json/json.go
index c7cf330e..11e72e64 100644
--- a/render/json.go
+++ b/render/json/json.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package render
+package json
import (
"bytes"
@@ -11,8 +11,42 @@ import (
"net/http"
"github.com/gin-gonic/gin/internal/json"
+ "github.com/gin-gonic/gin/render/common"
)
+func init() {
+ common.List["JSON"] = NewJSON
+ common.List["IndentedJSON"] = NewIndentedJSON
+ common.List["SecureJSON"] = NewSecureJSON
+ common.List["JsonpJSON"] = NewJsonpJSON
+ common.List["AsciiJSON"] = NewAsciiJSON
+}
+
+//NewJSON build a new JSON render
+func NewJSON(obj interface{}, _ map[string]string) common.Render {
+ return JSON{Data: obj}
+}
+
+//NewIndentedJSON build a new IndentedJSON render
+func NewIndentedJSON(obj interface{}, _ map[string]string) common.Render {
+ return IndentedJSON{Data: obj}
+}
+
+//NewSecureJSON build a new SecureJSON render
+func NewSecureJSON(obj interface{}, opts map[string]string) common.Render {
+ return SecureJSON{Prefix: opts["Prefix"], Data: obj}
+}
+
+//NewJsonpJSON build a new JsonpJSON render
+func NewJsonpJSON(obj interface{}, opts map[string]string) common.Render {
+ return JsonpJSON{Callback: opts["Callback"], Data: obj}
+}
+
+//NewAsciiJSON build a new AsciiJSON render
+func NewAsciiJSON(obj interface{}, _ map[string]string) common.Render {
+ return AsciiJSON{Data: obj}
+}
+
// JSON contains the given interface object.
type JSON struct {
Data interface{}
@@ -57,12 +91,12 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonContentType)
+ common.WriteContentType(w, jsonContentType)
}
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
- writeContentType(w, jsonContentType)
+ common.WriteContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj)
if err != nil {
return err
@@ -84,7 +118,7 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonContentType)
+ common.WriteContentType(w, jsonContentType)
}
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
@@ -107,7 +141,7 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonContentType)
+ common.WriteContentType(w, jsonContentType)
}
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
@@ -146,7 +180,7 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonpContentType)
+ common.WriteContentType(w, jsonpContentType)
}
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
@@ -172,5 +206,5 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonAsciiContentType)
+ common.WriteContentType(w, jsonAsciiContentType)
}
diff --git a/render/json_17.go b/render/json/json_17.go
similarity index 70%
rename from render/json_17.go
rename to render/json/json_17.go
index 208193c7..6f387e7a 100644
--- a/render/json_17.go
+++ b/render/json/json_17.go
@@ -4,14 +4,24 @@
// +build go1.7
-package render
+package json
import (
"net/http"
"github.com/gin-gonic/gin/internal/json"
+ "github.com/gin-gonic/gin/render/common"
)
+func init() {
+ common.List["PureJSON"] = NewPureJSON
+}
+
+//NewPureJSON build a new PureJSON render
+func NewPureJSON(obj interface{}, _ map[string]string) common.Render {
+ return PureJSON{Data: obj}
+}
+
// PureJSON contains the given interface object.
type PureJSON struct {
Data interface{}
@@ -27,5 +37,5 @@ func (r PureJSON) Render(w http.ResponseWriter) error {
// WriteContentType (PureJSON) writes custom ContentType.
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, jsonContentType)
+ common.WriteContentType(w, jsonContentType)
}
diff --git a/render/msgpack.go b/render/msgpack/msgpack.go
similarity index 72%
rename from render/msgpack.go
rename to render/msgpack/msgpack.go
index dc681fcf..5b390b85 100644
--- a/render/msgpack.go
+++ b/render/msgpack/msgpack.go
@@ -2,14 +2,19 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package render
+package msgpack
import (
"net/http"
+ "github.com/gin-gonic/gin/render/common"
"github.com/ugorji/go/codec"
)
+func init() {
+ common.List["MsgPack"] = NewMsgPack
+}
+
// MsgPack contains the given interface object.
type MsgPack struct {
Data interface{}
@@ -19,7 +24,7 @@ var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, msgpackContentType)
+ common.WriteContentType(w, msgpackContentType)
}
// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
@@ -29,7 +34,12 @@ func (r MsgPack) Render(w http.ResponseWriter) error {
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
- writeContentType(w, msgpackContentType)
+ common.WriteContentType(w, msgpackContentType)
var mh codec.MsgpackHandle
return codec.NewEncoder(w, &mh).Encode(obj)
}
+
+//NewMsgPack build a new MsgPack render
+func NewMsgPack(obj interface{}, _ map[string]string) common.Render {
+ return MsgPack{Data: obj}
+}
diff --git a/render/protobuf.go b/render/protobuf/protobuf.go
similarity index 72%
rename from render/protobuf.go
rename to render/protobuf/protobuf.go
index 15aca995..9a2b9f1a 100644
--- a/render/protobuf.go
+++ b/render/protobuf/protobuf.go
@@ -2,14 +2,19 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package render
+package protobuf
import (
"net/http"
+ "github.com/gin-gonic/gin/render/common"
"github.com/golang/protobuf/proto"
)
+func init() {
+ common.List["ProtoBuf"] = NewProtoBuf
+}
+
// ProtoBuf contains the given interface object.
type ProtoBuf struct {
Data interface{}
@@ -32,5 +37,10 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, protobufContentType)
+ common.WriteContentType(w, protobufContentType)
+}
+
+//NewProtoBuf build a new ProtoBuf render
+func NewProtoBuf(obj interface{}, _ map[string]string) common.Render {
+ return ProtoBuf{Data: obj}
}
diff --git a/render/reader.go b/render/reader.go
index 312af741..80b2485c 100644
--- a/render/reader.go
+++ b/render/reader.go
@@ -8,6 +8,8 @@ import (
"io"
"net/http"
"strconv"
+
+ "github.com/gin-gonic/gin/render/common"
)
// Reader contains the IO reader and its length, and custom ContentType and other headers.
@@ -29,7 +31,7 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
// WriteContentType (Reader) writes custom ContentType.
func (r Reader) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, []string{r.ContentType})
+ common.WriteContentType(w, []string{r.ContentType})
}
// writeHeaders writes custom Header.
diff --git a/render/render.go b/render/render.go
index abfc79fc..27d2a6b9 100644
--- a/render/render.go
+++ b/render/render.go
@@ -4,38 +4,76 @@
package render
-import "net/http"
+import (
+ "fmt"
-// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
-type Render interface {
- // Render writes data with custom ContentType.
- Render(http.ResponseWriter) error
- // WriteContentType writes custom ContentType.
- WriteContentType(w http.ResponseWriter)
-}
-
-var (
- _ Render = JSON{}
- _ Render = IndentedJSON{}
- _ Render = SecureJSON{}
- _ Render = JsonpJSON{}
- _ Render = XML{}
- _ Render = String{}
- _ Render = Redirect{}
- _ Render = Data{}
- _ Render = HTML{}
- _ HTMLRender = HTMLDebug{}
- _ HTMLRender = HTMLProduction{}
- _ Render = YAML{}
- _ Render = MsgPack{}
- _ Render = Reader{}
- _ Render = AsciiJSON{}
- _ Render = ProtoBuf{}
+ "github.com/gin-gonic/gin/render/common"
)
-func writeContentType(w http.ResponseWriter, value []string) {
- header := w.Header()
- if val := header["Content-Type"]; len(val) == 0 {
- header["Content-Type"] = value
- }
+var (
+ _ common.Render = String{}
+ _ common.Render = Redirect{}
+ _ common.Render = Data{}
+ _ common.Render = HTML{}
+ _ HTMLRender = HTMLDebug{}
+ _ HTMLRender = HTMLProduction{}
+ _ common.Render = Reader{}
+)
+
+//YAML return the render for yaml if loaded
+func YAML(obj interface{}) common.Render {
+ return retRender("YAML", obj, nil)
+}
+
+//XML return the render for xml if loaded
+func XML(obj interface{}) common.Render {
+ return retRender("XML", obj, nil)
+}
+
+//ProtoBuf return the render for ProtoBuf if loaded
+func ProtoBuf(obj interface{}) common.Render {
+ return retRender("ProtoBuf", obj, nil)
+}
+
+//MsgPack return the render for MsgPack if loaded
+func MsgPack(obj interface{}) common.Render {
+ return retRender("MsgPack", obj, nil)
+}
+
+//JSON return the render for JSON if loaded
+func JSON(obj interface{}) common.Render {
+ return retRender("JSON", obj, nil)
+}
+
+//IndentedJSON return the render for IndentedJSON if loaded
+func IndentedJSON(obj interface{}) common.Render {
+ return retRender("IndentedJSON", obj, nil)
+}
+
+//SecureJSON return the render for SecureJSON if loaded
+func SecureJSON(prefix string, obj interface{}) common.Render {
+ return retRender("SecureJSON", obj, map[string]string{
+ "Prefix": prefix,
+ })
+}
+
+//JsonpJSON return the render for JsonpJSON if loaded
+func JsonpJSON(callback string, obj interface{}) common.Render {
+ return retRender("JsonpJSON", obj, map[string]string{
+ "Callback": callback,
+ })
+}
+
+//AsciiJSON return the render for AsciiJSON if loaded
+func AsciiJSON(obj interface{}) common.Render {
+ return retRender("AsciiJSON", obj, nil)
+}
+
+//Search for a render
+func retRender(rID string, obj interface{}, opts map[string]string) common.Render {
+ r, ok := common.List[rID]
+ if !ok {
+ panic(fmt.Sprintf("Undefined render %s", rID))
+ }
+ return r(obj, opts)
}
diff --git a/render/render_17.go b/render/render_17.go
new file mode 100644
index 00000000..544c16b3
--- /dev/null
+++ b/render/render_17.go
@@ -0,0 +1,10 @@
+// +build go1.7
+
+package render
+
+import "github.com/gin-gonic/gin/render/common"
+
+//PureJSON return the render for AsciiJSON if loaded
+func PureJSON(obj interface{}) common.Render {
+ return retRender("PureJSON", obj, nil)
+}
diff --git a/render/render_17_test.go b/render/render_17_test.go
index 68330090..2b026ee6 100644
--- a/render/render_17_test.go
+++ b/render/render_17_test.go
@@ -19,7 +19,7 @@ func TestRenderPureJSON(t *testing.T) {
"foo": "bar",
"html": "",
}
- err := (PureJSON{data}).Render(w)
+ err := PureJSON(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
diff --git a/render/render_test.go b/render/render_test.go
index 76e29eeb..be65682d 100644
--- a/render/render_test.go
+++ b/render/render_test.go
@@ -20,6 +20,12 @@ import (
"github.com/ugorji/go/codec"
testdata "github.com/gin-gonic/gin/testdata/protoexample"
+
+ _ "github.com/gin-gonic/gin/render/json"
+ _ "github.com/gin-gonic/gin/render/msgpack"
+ _ "github.com/gin-gonic/gin/render/protobuf"
+ _ "github.com/gin-gonic/gin/render/xml"
+ _ "github.com/gin-gonic/gin/render/yaml"
)
// TODO unit tests
@@ -31,10 +37,10 @@ func TestRenderMsgPack(t *testing.T) {
"foo": "bar",
}
- (MsgPack{data}).WriteContentType(w)
+ MsgPack(data).WriteContentType(w)
assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
- err := (MsgPack{data}).Render(w)
+ err := MsgPack(data).Render(w)
assert.NoError(t, err)
@@ -56,10 +62,10 @@ func TestRenderJSON(t *testing.T) {
"html": "",
}
- (JSON{data}).WriteContentType(w)
+ JSON(data).WriteContentType(w)
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
- err := (JSON{data}).Render(w)
+ err := JSON(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
@@ -71,7 +77,7 @@ func TestRenderJSONPanics(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
- assert.Panics(t, func() { assert.NoError(t, (JSON{data}).Render(w)) })
+ assert.Panics(t, func() { assert.NoError(t, JSON(data).Render(w)) })
}
func TestRenderIndentedJSON(t *testing.T) {
@@ -81,7 +87,7 @@ func TestRenderIndentedJSON(t *testing.T) {
"bar": "foo",
}
- err := (IndentedJSON{data}).Render(w)
+ err := IndentedJSON(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
@@ -93,7 +99,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
- err := (IndentedJSON{data}).Render(w)
+ err := IndentedJSON(data).Render(w)
assert.Error(t, err)
}
@@ -103,10 +109,10 @@ func TestRenderSecureJSON(t *testing.T) {
"foo": "bar",
}
- (SecureJSON{"while(1);", data}).WriteContentType(w1)
+ SecureJSON("while(1);", data).WriteContentType(w1)
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
- err1 := (SecureJSON{"while(1);", data}).Render(w1)
+ err1 := SecureJSON("while(1);", data).Render(w1)
assert.NoError(t, err1)
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
@@ -119,7 +125,7 @@ func TestRenderSecureJSON(t *testing.T) {
"bar": "foo",
}}
- err2 := (SecureJSON{"while(1);", datas}).Render(w2)
+ err2 := SecureJSON("while(1);", datas).Render(w2)
assert.NoError(t, err2)
assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
@@ -130,7 +136,7 @@ func TestRenderSecureJSONFail(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
- err := (SecureJSON{"while(1);", data}).Render(w)
+ err := SecureJSON("while(1);", data).Render(w)
assert.Error(t, err)
}
@@ -140,10 +146,10 @@ func TestRenderJsonpJSON(t *testing.T) {
"foo": "bar",
}
- (JsonpJSON{"x", data}).WriteContentType(w1)
+ JsonpJSON("x", data).WriteContentType(w1)
assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
- err1 := (JsonpJSON{"x", data}).Render(w1)
+ err1 := JsonpJSON("x", data).Render(w1)
assert.NoError(t, err1)
assert.Equal(t, "x({\"foo\":\"bar\"})", w1.Body.String())
@@ -156,7 +162,7 @@ func TestRenderJsonpJSON(t *testing.T) {
"bar": "foo",
}}
- err2 := (JsonpJSON{"x", datas}).Render(w2)
+ err2 := JsonpJSON("x", datas).Render(w2)
assert.NoError(t, err2)
assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}])", w2.Body.String())
assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
@@ -167,10 +173,10 @@ func TestRenderJsonpJSONError2(t *testing.T) {
data := map[string]interface{}{
"foo": "bar",
}
- (JsonpJSON{"", data}).WriteContentType(w)
+ JsonpJSON("", data).WriteContentType(w)
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
- e := (JsonpJSON{"", data}).Render(w)
+ e := JsonpJSON("", data).Render(w)
assert.NoError(t, e)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@@ -182,7 +188,7 @@ func TestRenderJsonpJSONFail(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
- err := (JsonpJSON{"x", data}).Render(w)
+ err := JsonpJSON("x", data).Render(w)
assert.Error(t, err)
}
@@ -193,7 +199,7 @@ func TestRenderAsciiJSON(t *testing.T) {
"tag": "
",
}
- err := (AsciiJSON{data1}).Render(w1)
+ err := AsciiJSON(data1).Render(w1)
assert.NoError(t, err)
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
@@ -202,7 +208,7 @@ func TestRenderAsciiJSON(t *testing.T) {
w2 := httptest.NewRecorder()
data2 := float64(3.1415926)
- err = (AsciiJSON{data2}).Render(w2)
+ err = AsciiJSON(data2).Render(w2)
assert.NoError(t, err)
assert.Equal(t, "3.1415926", w2.Body.String())
}
@@ -212,7 +218,7 @@ func TestRenderAsciiJSONFail(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
- assert.Error(t, (AsciiJSON{data}).Render(w))
+ assert.Error(t, AsciiJSON(data).Render(w))
}
type xmlmap map[string]interface{}
@@ -247,10 +253,10 @@ b:
c: 2
d: [3, 4]
`
- (YAML{data}).WriteContentType(w)
+ YAML(data).WriteContentType(w)
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
- err := (YAML{data}).Render(w)
+ err := YAML(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n", w.Body.String())
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
@@ -265,7 +271,7 @@ func (ft *fail) MarshalYAML() (interface{}, error) {
func TestRenderYAMLFail(t *testing.T) {
w := httptest.NewRecorder()
- err := (YAML{&fail{}}).Render(w)
+ err := YAML(&fail{}).Render(w)
assert.Error(t, err)
}
@@ -279,12 +285,12 @@ func TestRenderProtoBuf(t *testing.T) {
Reps: reps,
}
- (ProtoBuf{data}).WriteContentType(w)
+ ProtoBuf(data).WriteContentType(w)
protoData, err := proto.Marshal(data)
assert.NoError(t, err)
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
- err = (ProtoBuf{data}).Render(w)
+ err = ProtoBuf(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, string(protoData), w.Body.String())
@@ -294,7 +300,7 @@ func TestRenderProtoBuf(t *testing.T) {
func TestRenderProtoBufFail(t *testing.T) {
w := httptest.NewRecorder()
data := &testdata.Test{}
- err := (ProtoBuf{data}).Render(w)
+ err := ProtoBuf(data).Render(w)
assert.Error(t, err)
}
@@ -304,10 +310,10 @@ func TestRenderXML(t *testing.T) {
"foo": "bar",
}
- (XML{data}).WriteContentType(w)
+ XML(data).WriteContentType(w)
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
- err := (XML{data}).Render(w)
+ err := XML(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "", w.Body.String())
@@ -486,3 +492,14 @@ func TestRenderReader(t *testing.T) {
assert.Equal(t, headers["Content-Disposition"], w.Header().Get("Content-Disposition"))
assert.Equal(t, headers["x-request-id"], w.Header().Get("x-request-id"))
}
+
+func TestRenderPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("retRender did not panic")
+ }
+ }()
+
+ //Should panic
+ retRender("NotKnowRender", nil, nil)
+}
diff --git a/render/text.go b/render/text.go
index 4e52d4c5..cd42e388 100644
--- a/render/text.go
+++ b/render/text.go
@@ -8,6 +8,8 @@ import (
"fmt"
"io"
"net/http"
+
+ "github.com/gin-gonic/gin/render/common"
)
// String contains the given interface object slice and its format.
@@ -25,12 +27,12 @@ func (r String) Render(w http.ResponseWriter) error {
// WriteContentType (String) writes Plain ContentType.
func (r String) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, plainContentType)
+ common.WriteContentType(w, plainContentType)
}
// WriteString writes data according to its format and write custom ContentType.
func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) {
- writeContentType(w, plainContentType)
+ common.WriteContentType(w, plainContentType)
if len(data) > 0 {
_, err = fmt.Fprintf(w, format, data...)
return
diff --git a/render/xml.go b/render/xml/xml.go
similarity index 71%
rename from render/xml.go
rename to render/xml/xml.go
index cc5390a2..637604b6 100644
--- a/render/xml.go
+++ b/render/xml/xml.go
@@ -2,13 +2,19 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package render
+package xml
import (
"encoding/xml"
"net/http"
+
+ "github.com/gin-gonic/gin/render/common"
)
+func init() {
+ common.List["XML"] = NewXML
+}
+
// XML contains the given interface object.
type XML struct {
Data interface{}
@@ -24,5 +30,10 @@ func (r XML) Render(w http.ResponseWriter) error {
// WriteContentType (XML) writes XML ContentType for response.
func (r XML) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, xmlContentType)
+ common.WriteContentType(w, xmlContentType)
+}
+
+//NewXML build a new xml render
+func NewXML(obj interface{}, _ map[string]string) common.Render {
+ return XML{Data: obj}
}
diff --git a/render/yaml.go b/render/yaml/yaml.go
similarity index 73%
rename from render/yaml.go
rename to render/yaml/yaml.go
index 0df78360..58a6bbe4 100644
--- a/render/yaml.go
+++ b/render/yaml/yaml.go
@@ -2,14 +2,19 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
-package render
+package yaml
import (
"net/http"
+ "github.com/gin-gonic/gin/render/common"
"gopkg.in/yaml.v2"
)
+func init() {
+ common.List["YAML"] = NewYAML
+}
+
// YAML contains the given interface object.
type YAML struct {
Data interface{}
@@ -32,5 +37,10 @@ func (r YAML) Render(w http.ResponseWriter) error {
// WriteContentType (YAML) writes YAML ContentType for response.
func (r YAML) WriteContentType(w http.ResponseWriter) {
- writeContentType(w, yamlContentType)
+ common.WriteContentType(w, yamlContentType)
+}
+
+//NewYAML build a new yaml render
+func NewYAML(obj interface{}, _ map[string]string) common.Render {
+ return YAML{Data: obj}
}