Merge branch 'new-binding-validator' of https://github.com/zhing/gin into zhing-new-binding-validator
Conflicts: binding/binding.go binding/binding_test.go
This commit is contained in:
		| @ -14,6 +14,7 @@ const ( | |||||||
| 	MIMEPlain             = "text/plain" | 	MIMEPlain             = "text/plain" | ||||||
| 	MIMEPOSTForm          = "application/x-www-form-urlencoded" | 	MIMEPOSTForm          = "application/x-www-form-urlencoded" | ||||||
| 	MIMEMultipartPOSTForm = "multipart/form-data" | 	MIMEMultipartPOSTForm = "multipart/form-data" | ||||||
|  | 	MIMEPROTOBUF          = "application/x-protobuf" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type Binding interface { | type Binding interface { | ||||||
| @ -38,6 +39,7 @@ var ( | |||||||
| 	Form          = formBinding{} | 	Form          = formBinding{} | ||||||
| 	FormPost      = formPostBinding{} | 	FormPost      = formPostBinding{} | ||||||
| 	FormMultipart = formMultipartBinding{} | 	FormMultipart = formMultipartBinding{} | ||||||
|  | 	ProtoBuf      = protobufBinding{} | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Default(method, contentType string) Binding { | func Default(method, contentType string) Binding { | ||||||
| @ -49,6 +51,8 @@ func Default(method, contentType string) Binding { | |||||||
| 			return JSON | 			return JSON | ||||||
| 		case MIMEXML, MIMEXML2: | 		case MIMEXML, MIMEXML2: | ||||||
| 			return XML | 			return XML | ||||||
|  | 		case MIMEPROTOBUF: | ||||||
|  | 			return ProtoBuf | ||||||
| 		default: //case MIMEPOSTForm, MIMEMultipartPOSTForm: | 		default: //case MIMEPOSTForm, MIMEMultipartPOSTForm: | ||||||
| 			return Form | 			return Form | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -10,6 +10,9 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/gin-gonic/gin/binding/example" | ||||||
|  | 	"github.com/golang/protobuf/proto" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -37,6 +40,9 @@ func TestBindingDefault(t *testing.T) { | |||||||
|  |  | ||||||
| 	assert.Equal(t, Default("POST", MIMEMultipartPOSTForm), Form) | 	assert.Equal(t, Default("POST", MIMEMultipartPOSTForm), Form) | ||||||
| 	assert.Equal(t, Default("PUT", MIMEMultipartPOSTForm), Form) | 	assert.Equal(t, Default("PUT", MIMEMultipartPOSTForm), Form) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, Default("POST", MIMEPROTOBUF), ProtoBuf) | ||||||
|  | 	assert.Equal(t, Default("PUT", MIMEPROTOBUF), ProtoBuf) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestBindingJSON(t *testing.T) { | func TestBindingJSON(t *testing.T) { | ||||||
| @ -103,6 +109,18 @@ func TestBindingFormMultipart(t *testing.T) { | |||||||
| 	assert.Equal(t, obj.Bar, "foo") | 	assert.Equal(t, obj.Bar, "foo") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestBindingProtoBuf(t *testing.T) { | ||||||
|  | 	test := &example.Test{ | ||||||
|  | 		Label: proto.String("yes"), | ||||||
|  | 	} | ||||||
|  | 	data, _ := proto.Marshal(test) | ||||||
|  |  | ||||||
|  | 	testProtoBodyBinding(t, | ||||||
|  | 		ProtoBuf, "protobuf", | ||||||
|  | 		"/", "/", | ||||||
|  | 		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"}`) | ||||||
| @ -156,6 +174,23 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody | |||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { | ||||||
|  | 	assert.Equal(t, b.Name(), name) | ||||||
|  |  | ||||||
|  | 	obj := example.Test{} | ||||||
|  | 	req := requestWithBody("POST", path, body) | ||||||
|  | 	req.Header.Add("Content-Type", MIMEPROTOBUF) | ||||||
|  | 	err := b.Bind(req, &obj) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, *obj.Label, "yes") | ||||||
|  |  | ||||||
|  | 	obj = example.Test{} | ||||||
|  | 	req = requestWithBody("POST", badPath, badBody) | ||||||
|  | 	req.Header.Add("Content-Type", MIMEPROTOBUF) | ||||||
|  | 	err = ProtoBuf.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 | ||||||
|  | |||||||
							
								
								
									
										113
									
								
								binding/example/test.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								binding/example/test.pb.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | // Code generated by protoc-gen-go. | ||||||
|  | // source: test.proto | ||||||
|  | // DO NOT EDIT! | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Package example is a generated protocol buffer package. | ||||||
|  |  | ||||||
|  | It is generated from these files: | ||||||
|  | 	test.proto | ||||||
|  |  | ||||||
|  | It has these top-level messages: | ||||||
|  | 	Test | ||||||
|  | */ | ||||||
|  | package example | ||||||
|  |  | ||||||
|  | import proto "github.com/golang/protobuf/proto" | ||||||
|  | import math "math" | ||||||
|  |  | ||||||
|  | // Reference imports to suppress errors if they are not otherwise used. | ||||||
|  | var _ = proto.Marshal | ||||||
|  | var _ = math.Inf | ||||||
|  |  | ||||||
|  | type FOO int32 | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	FOO_X FOO = 17 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var FOO_name = map[int32]string{ | ||||||
|  | 	17: "X", | ||||||
|  | } | ||||||
|  | var FOO_value = map[string]int32{ | ||||||
|  | 	"X": 17, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (x FOO) Enum() *FOO { | ||||||
|  | 	p := new(FOO) | ||||||
|  | 	*p = x | ||||||
|  | 	return p | ||||||
|  | } | ||||||
|  | func (x FOO) String() string { | ||||||
|  | 	return proto.EnumName(FOO_name, int32(x)) | ||||||
|  | } | ||||||
|  | func (x *FOO) UnmarshalJSON(data []byte) error { | ||||||
|  | 	value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	*x = FOO(value) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Test struct { | ||||||
|  | 	Label            *string             `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` | ||||||
|  | 	Type             *int32              `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` | ||||||
|  | 	Reps             []int64             `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` | ||||||
|  | 	Optionalgroup    *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` | ||||||
|  | 	XXX_unrecognized []byte              `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Test) Reset()         { *m = Test{} } | ||||||
|  | func (m *Test) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*Test) ProtoMessage()    {} | ||||||
|  |  | ||||||
|  | const Default_Test_Type int32 = 77 | ||||||
|  |  | ||||||
|  | func (m *Test) GetLabel() string { | ||||||
|  | 	if m != nil && m.Label != nil { | ||||||
|  | 		return *m.Label | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Test) GetType() int32 { | ||||||
|  | 	if m != nil && m.Type != nil { | ||||||
|  | 		return *m.Type | ||||||
|  | 	} | ||||||
|  | 	return Default_Test_Type | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Test) GetReps() []int64 { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.Reps | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Test) GetOptionalgroup() *Test_OptionalGroup { | ||||||
|  | 	if m != nil { | ||||||
|  | 		return m.Optionalgroup | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Test_OptionalGroup struct { | ||||||
|  | 	RequiredField    *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` | ||||||
|  | 	XXX_unrecognized []byte  `json:"-"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *Test_OptionalGroup) Reset()         { *m = Test_OptionalGroup{} } | ||||||
|  | func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } | ||||||
|  | func (*Test_OptionalGroup) ProtoMessage()    {} | ||||||
|  |  | ||||||
|  | func (m *Test_OptionalGroup) GetRequiredField() string { | ||||||
|  | 	if m != nil && m.RequiredField != nil { | ||||||
|  | 		return *m.RequiredField | ||||||
|  | 	} | ||||||
|  | 	return "" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	proto.RegisterEnum("example.FOO", FOO_name, FOO_value) | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								binding/example/test.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								binding/example/test.proto
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | package example; | ||||||
|  |  | ||||||
|  | enum FOO {X=17;}; | ||||||
|  |  | ||||||
|  | message Test { | ||||||
|  |    required string label = 1; | ||||||
|  |    optional int32 type = 2[default=77]; | ||||||
|  |    repeated int64 reps = 3; | ||||||
|  |    optional group OptionalGroup = 4{ | ||||||
|  |      required string RequiredField = 5; | ||||||
|  |    } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								binding/protobuf.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								binding/protobuf.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | // Copyright 2014 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 ( | ||||||
|  | 	"github.com/golang/protobuf/proto" | ||||||
|  |  | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type protobufBinding struct{} | ||||||
|  |  | ||||||
|  | func (_ protobufBinding) Name() string { | ||||||
|  | 	return "protobuf" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (_ protobufBinding) Bind(req *http.Request, obj interface{}) error { | ||||||
|  |  | ||||||
|  | 	buf, err := ioutil.ReadAll(req.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = proto.Unmarshal(buf, 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 | ||||||
|  | 	return nil | ||||||
|  | 	//return validate(obj) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user