浏览代码

Added support for MessagePack binding and rendering (#808)

Added deps to vendor.json and fixed rendering bug
Harindu Perera 8 年之前
父节点
当前提交
5be2123c1a
共有 7 个文件被更改,包括 138 次插入2 次删除
  1. 5 0
      binding/binding.go
  2. 43 2
      binding/binding_test.go
  3. 28 0
      binding/msgpack.go
  4. 31 0
      render/msgpack.go
  5. 2 0
      render/render.go
  6. 23 0
      render/render_test.go
  7. 6 0
      vendor/vendor.json

+ 5 - 0
binding/binding.go

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

+ 43 - 2
binding/binding_test.go

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

+ 28 - 0
binding/msgpack.go

@@ -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 - 0
render/msgpack.go

@@ -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 - 0
render/render.go

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

+ 23 - 0
render/render_test.go

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

+ 6 - 0
vendor/vendor.json

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