diff --git a/binding/binding.go b/binding/binding.go
index f719fbc1..38d45e9a 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -14,6 +14,7 @@ const (
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
+ MIMEPROTOBUF = "application/octet-stream"
)
type Binding interface {
@@ -33,9 +34,10 @@ type StructValidator interface {
var Validator StructValidator = &defaultValidator{}
var (
- JSON = jsonBinding{}
- XML = xmlBinding{}
- Form = formBinding{}
+ JSON = jsonBinding{}
+ XML = xmlBinding{}
+ Form = formBinding{}
+ ProtoBuf = protobufBinding{}
)
func Default(method, contentType string) Binding {
@@ -47,6 +49,8 @@ func Default(method, contentType string) Binding {
return JSON
case MIMEXML, MIMEXML2:
return XML
+ case MIMEPROTOBUF:
+ return ProtoBuf
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
return Form
}
diff --git a/binding/binding_test.go b/binding/binding_test.go
index db1678e4..517be437 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -6,6 +6,8 @@ package binding
import (
"bytes"
+ "github.com/gin-gonic/gin/binding/example"
+ "github.com/golang/protobuf/proto"
"net/http"
"testing"
@@ -36,6 +38,9 @@ func TestBindingDefault(t *testing.T) {
assert.Equal(t, Default("POST", 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) {
@@ -64,6 +69,18 @@ func TestBindingXML(t *testing.T) {
"", "")
}
+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) {
var obj FooStruct
req := requestWithBody("POST", "/", `{"bar": "foo"}`)
@@ -117,6 +134,23 @@ func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody
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) {
req, _ = http.NewRequest(method, path, bytes.NewBufferString(body))
return
diff --git a/binding/example/test.pb.go b/binding/example/test.pb.go
new file mode 100644
index 00000000..3de8444f
--- /dev/null
+++ b/binding/example/test.pb.go
@@ -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)
+}
diff --git a/binding/example/test.proto b/binding/example/test.proto
new file mode 100644
index 00000000..8ee9800a
--- /dev/null
+++ b/binding/example/test.proto
@@ -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;
+ }
+}
diff --git a/binding/protobuf.go b/binding/protobuf.go
new file mode 100644
index 00000000..d6bef029
--- /dev/null
+++ b/binding/protobuf.go
@@ -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)
+}