mirror of https://github.com/gin-gonic/gin.git
Added support for MessagePack binding and rendering (#808)
Added deps to vendor.json and fixed rendering bug
This commit is contained in:
parent
863248034b
commit
5be2123c1a
|
@ -15,6 +15,8 @@ const (
|
||||||
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
MIMEPOSTForm = "application/x-www-form-urlencoded"
|
||||||
MIMEMultipartPOSTForm = "multipart/form-data"
|
MIMEMultipartPOSTForm = "multipart/form-data"
|
||||||
MIMEPROTOBUF = "application/x-protobuf"
|
MIMEPROTOBUF = "application/x-protobuf"
|
||||||
|
MIMEMSGPACK = "application/x-msgpack"
|
||||||
|
MIMEMSGPACK2 = "application/msgpack"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Binding interface {
|
type Binding interface {
|
||||||
|
@ -40,6 +42,7 @@ var (
|
||||||
FormPost = formPostBinding{}
|
FormPost = formPostBinding{}
|
||||||
FormMultipart = formMultipartBinding{}
|
FormMultipart = formMultipartBinding{}
|
||||||
ProtoBuf = protobufBinding{}
|
ProtoBuf = protobufBinding{}
|
||||||
|
MsgPack = msgpackBinding{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Default(method, contentType string) Binding {
|
func Default(method, contentType string) Binding {
|
||||||
|
@ -53,6 +56,8 @@ func Default(method, contentType string) Binding {
|
||||||
return XML
|
return XML
|
||||||
case MIMEPROTOBUF:
|
case MIMEPROTOBUF:
|
||||||
return ProtoBuf
|
return ProtoBuf
|
||||||
|
case MIMEMSGPACK, MIMEMSGPACK2:
|
||||||
|
return MsgPack
|
||||||
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
|
||||||
return Form
|
return Form
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,17 +12,18 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/binding/example"
|
"github.com/gin-gonic/gin/binding/example"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FooStruct struct {
|
type FooStruct struct {
|
||||||
Foo string `json:"foo" form:"foo" xml:"foo" binding:"required"`
|
Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooBarStruct struct {
|
type FooBarStruct struct {
|
||||||
FooStruct
|
FooStruct
|
||||||
Bar string `json:"bar" form:"bar" xml:"bar" binding:"required"`
|
Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBindingDefault(t *testing.T) {
|
func TestBindingDefault(t *testing.T) {
|
||||||
|
@ -43,6 +44,9 @@ func TestBindingDefault(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, Default("POST", MIMEPROTOBUF), ProtoBuf)
|
assert.Equal(t, Default("POST", MIMEPROTOBUF), ProtoBuf)
|
||||||
assert.Equal(t, Default("PUT", MIMEPROTOBUF), ProtoBuf)
|
assert.Equal(t, Default("PUT", MIMEPROTOBUF), ProtoBuf)
|
||||||
|
|
||||||
|
assert.Equal(t, Default("POST", MIMEMSGPACK), MsgPack)
|
||||||
|
assert.Equal(t, Default("PUT", MIMEMSGPACK2), MsgPack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBindingJSON(t *testing.T) {
|
func TestBindingJSON(t *testing.T) {
|
||||||
|
@ -121,6 +125,26 @@ func TestBindingProtoBuf(t *testing.T) {
|
||||||
string(data), string(data[1:]))
|
string(data), string(data[1:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindingMsgPack(t *testing.T) {
|
||||||
|
test := FooStruct{
|
||||||
|
Foo: "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
h := new(codec.MsgpackHandle)
|
||||||
|
assert.NotNil(t, h)
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
assert.NotNil(t, buf)
|
||||||
|
err := codec.NewEncoder(buf, h).Encode(test)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
data := buf.Bytes()
|
||||||
|
|
||||||
|
testMsgPackBodyBinding(t,
|
||||||
|
MsgPack, "msgpack",
|
||||||
|
"/", "/",
|
||||||
|
string(data), string(data[1:]))
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidationFails(t *testing.T) {
|
func TestValidationFails(t *testing.T) {
|
||||||
var obj FooStruct
|
var obj FooStruct
|
||||||
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
|
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
|
||||||
|
@ -213,6 +237,23 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
|
assert.Equal(t, b.Name(), name)
|
||||||
|
|
||||||
|
obj := FooStruct{}
|
||||||
|
req := requestWithBody("POST", path, body)
|
||||||
|
req.Header.Add("Content-Type", MIMEMSGPACK)
|
||||||
|
err := b.Bind(req, &obj)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, obj.Foo, "bar")
|
||||||
|
|
||||||
|
obj = FooStruct{}
|
||||||
|
req = requestWithBody("POST", badPath, badBody)
|
||||||
|
req.Header.Add("Content-Type", MIMEMSGPACK)
|
||||||
|
err = MsgPack.Bind(req, &obj)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func requestWithBody(method, path, body string) (req *http.Request) {
|
func requestWithBody(method, path, body string) (req *http.Request) {
|
||||||
req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
|
req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type msgpackBinding struct{}
|
||||||
|
|
||||||
|
func (msgpackBinding) Name() string {
|
||||||
|
return "msgpack"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
|
||||||
|
if err := codec.NewDecoder(req.Body, new(codec.MsgpackHandle)).Decode(&obj); err != nil {
|
||||||
|
//var decoder *codec.Decoder = codec.NewDecoder(req.Body, &codec.MsgpackHandle)
|
||||||
|
//if err := decoder.Decode(&obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MsgPack struct {
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
||||||
|
|
||||||
|
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
|
||||||
|
writeContentType(w, msgpackContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r MsgPack) Render(w http.ResponseWriter) error {
|
||||||
|
return WriteMsgPack(w, r.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
||||||
|
writeContentType(w, msgpackContentType)
|
||||||
|
var h codec.Handle = new(codec.MsgpackHandle)
|
||||||
|
return codec.NewEncoder(w, h).Encode(obj)
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ var (
|
||||||
_ HTMLRender = HTMLDebug{}
|
_ HTMLRender = HTMLDebug{}
|
||||||
_ HTMLRender = HTMLProduction{}
|
_ HTMLRender = HTMLProduction{}
|
||||||
_ Render = YAML{}
|
_ Render = YAML{}
|
||||||
|
_ Render = MsgPack{}
|
||||||
|
_ Render = MsgPack{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func writeContentType(w http.ResponseWriter, value []string) {
|
func writeContentType(w http.ResponseWriter, value []string) {
|
||||||
|
|
|
@ -5,17 +5,40 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/ugorji/go/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO unit tests
|
// TODO unit tests
|
||||||
// test errors
|
// test errors
|
||||||
|
|
||||||
|
func TestRenderMsgPack(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := (MsgPack{data}).Render(w)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
h := new(codec.MsgpackHandle)
|
||||||
|
assert.NotNil(t, h)
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
assert.NotNil(t, buf)
|
||||||
|
err = codec.NewEncoder(buf, h).Encode(data)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Body.String(), string(buf.Bytes()))
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/msgpack; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
func TestRenderJSON(t *testing.T) {
|
func TestRenderJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
|
|
|
@ -47,6 +47,12 @@
|
||||||
"revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506",
|
"revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506",
|
||||||
"revisionTime": "2016-09-25T22:06:09Z"
|
"revisionTime": "2016-09-25T22:06:09Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "CoxdaTYdPZNJXr8mJfLxye428N0=",
|
||||||
|
"path": "github.com/ugorji/go/codec",
|
||||||
|
"revision": "c88ee250d0221a57af388746f5cf03768c21d6e2",
|
||||||
|
"revisionTime": "2017-02-15T20:11:44Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
||||||
"comment": "release-branch.go1.7",
|
"comment": "release-branch.go1.7",
|
||||||
|
|
Loading…
Reference in New Issue