Skip to content

Commit

Permalink
Added support for MessagePack binding and rendering (gin-gonic#808)
Browse files Browse the repository at this point in the history
Added deps to vendor.json and fixed rendering bug
  • Loading branch information
harindup authored and appleboy committed Feb 23, 2017
1 parent 8632480 commit 5be2123
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 2 deletions.
5 changes: 5 additions & 0 deletions binding/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const (
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
)

type Binding interface {
Expand All @@ -40,6 +42,7 @@ var (
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
)

func Default(method, contentType string) Binding {
Expand All @@ -53,6 +56,8 @@ func Default(method, contentType string) Binding {
return XML
case MIMEPROTOBUF:
return ProtoBuf
case MIMEMSGPACK, MIMEMSGPACK2:
return MsgPack
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
return Form
}
Expand Down
45 changes: 43 additions & 2 deletions binding/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ import (

"github.com/gin-gonic/gin/binding/example"
"github.com/golang/protobuf/proto"
"github.com/ugorji/go/codec"

"github.com/stretchr/testify/assert"
)

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 {
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) {
Expand All @@ -43,6 +44,9 @@ func TestBindingDefault(t *testing.T) {

assert.Equal(t, Default("POST", 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) {
Expand Down Expand Up @@ -121,6 +125,26 @@ func TestBindingProtoBuf(t *testing.T) {
string(data), string(data[1:]))
}

func TestBindingMsgPack(t *testing.T) {
test := FooStruct{
Foo: "bar",
}

h := new(codec.MsgpackHandle)
assert.NotNil(t, h)
buf := bytes.NewBuffer([]byte{})
assert.NotNil(t, buf)
err := codec.NewEncoder(buf, h).Encode(test)
assert.NoError(t, err)

data := buf.Bytes()

testMsgPackBodyBinding(t,
MsgPack, "msgpack",
"/", "/",
string(data), string(data[1:]))
}

func TestValidationFails(t *testing.T) {
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
Expand Down Expand Up @@ -213,6 +237,23 @@ func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, ba
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) {
req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
return
Expand Down
28 changes: 28 additions & 0 deletions binding/msgpack.go
Original file line number Diff line number Diff line change
@@ -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)

}
31 changes: 31 additions & 0 deletions render/msgpack.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 2 additions & 0 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ var (
_ HTMLRender = HTMLDebug{}
_ HTMLRender = HTMLProduction{}
_ Render = YAML{}
_ Render = MsgPack{}
_ Render = MsgPack{}
)

func writeContentType(w http.ResponseWriter, value []string) {
Expand Down
23 changes: 23 additions & 0 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,40 @@
package render

import (
"bytes"
"encoding/xml"
"html/template"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/ugorji/go/codec"
)

// TODO unit tests
// 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) {
w := httptest.NewRecorder()
data := map[string]interface{}{
Expand Down
6 changes: 6 additions & 0 deletions vendor/vendor.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
"revision": "976c720a22c8eb4eb6a0b4348ad85ad12491a506",
"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=",
"comment": "release-branch.go1.7",
Expand Down

0 comments on commit 5be2123

Please sign in to comment.