Added support for MessagePack binding and rendering (#808)
Added deps to vendor.json and fixed rendering bug
This commit is contained in:
		| @ -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 | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								binding/msgpack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								binding/msgpack.go
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								render/msgpack.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								render/msgpack.go
									
									
									
									
									
										Normal file
									
								
							| @ -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{}{ | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							| @ -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", | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user