binding/render: modular deps

This commit is contained in:
Antoine GIRARD 2019-04-17 16:10:21 +02:00
parent ffcbe77b1e
commit 54eda7fe73
No known key found for this signature in database
GPG Key ID: F3B46D1708E22963
36 changed files with 719 additions and 398 deletions

View File

@ -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
}

View File

@ -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: `<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo>FOO</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)
}

View File

@ -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",
"/", "/",
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
}
func TestBindingXMLFail(t *testing.T) {
testBodyBindingFail(t,
XML, "xml",
XML(), "xml",
"/", "/",
"<map><foo>bar<foo></map>", "<map><bar>foo</bar></map>")
}
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")
}

78
binding/common/common.go Normal file
View File

@ -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)
}

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -18,21 +18,15 @@ 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"
)
@ -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)

View File

@ -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))
}

View File

@ -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>FOO</foo>
<bar>BAR</bar>
</root>`))
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>FOO</foo>
<bar>BAR</bar>
</root>`))
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: `<?xml version="1.0" encoding="UTF-8"?>
<root>
@ -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: `<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo>FOO</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)

View File

@ -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

4
gin.go
View File

@ -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

View File

@ -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.

View File

@ -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
}

24
render/common/common.go Normal file
View File

@ -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{}

View File

@ -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})
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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}
}

View File

@ -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}
}

View File

@ -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.

View File

@ -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)
}

10
render/render_17.go Normal file
View File

@ -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)
}

View File

@ -19,7 +19,7 @@ func TestRenderPureJSON(t *testing.T) {
"foo": "bar",
"html": "<b>",
}
err := (PureJSON{data}).Render(w)
err := PureJSON(data).Render(w)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))

View File

@ -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": "<b>",
}
(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": "<br>",
}
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, "<map><foo>bar</foo></map>", 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)
}

View File

@ -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

View File

@ -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}
}

View File

@ -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}
}