* Add interface to read body bytes in binding * Add BindingBody implementation for some binding * Fix to use `BindBodyBytesKey` for key * Revert "Fix to use `BindBodyBytesKey` for key" This reverts commit 2c82901ceab6ae53730a3cfcd9839bee11a08f13. * Use private-like key for body bytes * Add tests for BindingBody & ShouldBindBodyWith * Add note for README * Remove redundant space between sentences
This commit is contained in:
		
				
					committed by
					
						 Bo-Yi Wu
						Bo-Yi Wu
					
				
			
			
				
	
			
			
			
						parent
						
							6e09ef03b0
						
					
				
				
					commit
					995fa8e9ce
				
			| @ -29,6 +29,13 @@ type Binding interface { | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| // 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 reqest. Gin provides a default implementation for this using | ||||
|  | ||||
							
								
								
									
										67
									
								
								binding/binding_body_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								binding/binding_body_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| package binding | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin/binding/example" | ||||
| 	"github.com/golang/protobuf/proto" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/ugorji/go/codec" | ||||
| ) | ||||
|  | ||||
| func TestBindingBody(t *testing.T) { | ||||
| 	for _, tt := range []struct { | ||||
| 		name    string | ||||
| 		binding BindingBody | ||||
| 		body    string | ||||
| 		want    string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:    "JSON bidning", | ||||
| 			binding: JSON, | ||||
| 			body:    `{"foo":"FOO"}`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "XML bidning", | ||||
| 			binding: XML, | ||||
| 			body: `<?xml version="1.0" encoding="UTF-8"?> | ||||
| <root> | ||||
|    <foo>FOO</foo> | ||||
| </root>`, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:    "MsgPack binding", | ||||
| 			binding: MsgPack, | ||||
| 			body:    msgPackBody(t), | ||||
| 		}, | ||||
| 	} { | ||||
| 		t.Logf("testing: %s", tt.name) | ||||
| 		req := requestWithBody("POST", "/", tt.body) | ||||
| 		form := FooStruct{} | ||||
| 		body, _ := ioutil.ReadAll(req.Body) | ||||
| 		assert.NoError(t, tt.binding.BindBody(body, &form)) | ||||
| 		assert.Equal(t, FooStruct{"FOO"}, form) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func msgPackBody(t *testing.T) string { | ||||
| 	test := FooStruct{"FOO"} | ||||
| 	h := new(codec.MsgpackHandle) | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
| 	assert.NoError(t, codec.NewEncoder(buf, h).Encode(test)) | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| func TestBindingBodyProto(t *testing.T) { | ||||
| 	test := example.Test{ | ||||
| 		Label: proto.String("FOO"), | ||||
| 	} | ||||
| 	data, _ := proto.Marshal(&test) | ||||
| 	req := requestWithBody("POST", "/", string(data)) | ||||
| 	form := example.Test{} | ||||
| 	body, _ := ioutil.ReadAll(req.Body) | ||||
| 	assert.NoError(t, ProtoBuf.BindBody(body, &form)) | ||||
| 	assert.Equal(t, test, form) | ||||
| } | ||||
| @ -5,6 +5,8 @@ | ||||
| package binding | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin/json" | ||||
| @ -22,7 +24,15 @@ func (jsonBinding) Name() string { | ||||
| } | ||||
|  | ||||
| func (jsonBinding) Bind(req *http.Request, obj interface{}) error { | ||||
| 	decoder := json.NewDecoder(req.Body) | ||||
| 	return decodeJSON(req.Body, obj) | ||||
| } | ||||
|  | ||||
| func (jsonBinding) BindBody(body []byte, obj interface{}) error { | ||||
| 	return decodeJSON(bytes.NewReader(body), obj) | ||||
| } | ||||
|  | ||||
| func decodeJSON(r io.Reader, obj interface{}) error { | ||||
| 	decoder := json.NewDecoder(r) | ||||
| 	if EnableDecoderUseNumber { | ||||
| 		decoder.UseNumber() | ||||
| 	} | ||||
|  | ||||
| @ -5,6 +5,8 @@ | ||||
| package binding | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/ugorji/go/codec" | ||||
| @ -17,7 +19,16 @@ func (msgpackBinding) Name() string { | ||||
| } | ||||
|  | ||||
| func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { | ||||
| 	if err := codec.NewDecoder(req.Body, new(codec.MsgpackHandle)).Decode(&obj); err != nil { | ||||
| 	return decodeMsgPack(req.Body, obj) | ||||
| } | ||||
|  | ||||
| func (msgpackBinding) BindBody(body []byte, obj interface{}) error { | ||||
| 	return decodeMsgPack(bytes.NewReader(body), obj) | ||||
| } | ||||
|  | ||||
| func decodeMsgPack(r io.Reader, obj interface{}) error { | ||||
| 	cdc := new(codec.MsgpackHandle) | ||||
| 	if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return validate(obj) | ||||
|  | ||||
| @ -17,19 +17,20 @@ func (protobufBinding) Name() string { | ||||
| 	return "protobuf" | ||||
| } | ||||
|  | ||||
| func (protobufBinding) Bind(req *http.Request, obj interface{}) error { | ||||
|  | ||||
| func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { | ||||
| 	buf, err := ioutil.ReadAll(req.Body) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return b.BindBody(buf, obj) | ||||
| } | ||||
|  | ||||
| 	if err = proto.Unmarshal(buf, obj.(proto.Message)); err != nil { | ||||
| func (protobufBinding) BindBody(body []byte, obj interface{}) error { | ||||
| 	if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	//Here it's same to return validate(obj), but util now we cann't add `binding:""` to the struct | ||||
| 	//which automatically generate by gen-proto | ||||
| 	// Here it's same to return validate(obj), but util now we cann't add | ||||
| 	// `binding:""` to the struct which automatically generate by gen-proto | ||||
| 	return nil | ||||
| 	//return validate(obj) | ||||
| 	// return validate(obj) | ||||
| } | ||||
|  | ||||
| @ -5,7 +5,9 @@ | ||||
| package binding | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/xml" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| @ -16,7 +18,14 @@ func (xmlBinding) Name() string { | ||||
| } | ||||
|  | ||||
| func (xmlBinding) Bind(req *http.Request, obj interface{}) error { | ||||
| 	decoder := xml.NewDecoder(req.Body) | ||||
| 	return decodeXML(req.Body, obj) | ||||
| } | ||||
|  | ||||
| func (xmlBinding) BindBody(body []byte, obj interface{}) error { | ||||
| 	return decodeXML(bytes.NewReader(body), obj) | ||||
| } | ||||
| func decodeXML(r io.Reader, obj interface{}) error { | ||||
| 	decoder := xml.NewDecoder(r) | ||||
| 	if err := decoder.Decode(obj); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user