diff --git a/README.md b/README.md
index 40f4d8a8..e6eac123 100644
--- a/README.md
+++ b/README.md
@@ -530,7 +530,7 @@ func main() {
### Model binding and validation
-To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
+To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).
@@ -538,10 +538,10 @@ Note that you need to set the corresponding binding tag on all fields you want t
Also, Gin provides two sets of methods for binding:
- **Type** - Must bind
- - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`
+ - **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
- **Type** - Should bind
- - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`
+ - **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
@@ -859,12 +859,12 @@ form.html
```
@@ -1057,7 +1057,7 @@ func main() {
})
// listen and serve on 0.0.0.0:8080
- r.Run(":8080)
+ r.Run(":8080")
}
```
@@ -1195,7 +1195,7 @@ You may use custom delims
```go
r := gin.Default()
r.Delims("{[{", "}]}")
- r.LoadHTMLGlob("/path/to/templates"))
+ r.LoadHTMLGlob("/path/to/templates")
```
#### Custom Template Funcs
diff --git a/binding/binding.go b/binding/binding.go
index 8669ab1e..26d71c9f 100644
--- a/binding/binding.go
+++ b/binding/binding.go
@@ -18,6 +18,7 @@ const (
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
+ MIMEYAML = "application/x-yaml"
)
// Binding describes the interface which needs to be implemented for binding the
@@ -44,7 +45,7 @@ type BindingUri interface {
// 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
+// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v8.18.2.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
@@ -75,6 +76,7 @@ var (
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
+ YAML = yamlBinding{}
Uri = uriBinding{}
)
@@ -94,6 +96,8 @@ func Default(method, contentType string) Binding {
return ProtoBuf
case MIMEMSGPACK, MIMEMSGPACK2:
return MsgPack
+ case MIMEYAML:
+ return YAML
default: //case MIMEPOSTForm, MIMEMultipartPOSTForm:
return Form
}
diff --git a/binding/binding_body_test.go b/binding/binding_body_test.go
index dfd761e1..901d429c 100644
--- a/binding/binding_body_test.go
+++ b/binding/binding_body_test.go
@@ -19,12 +19,12 @@ func TestBindingBody(t *testing.T) {
want string
}{
{
- name: "JSON bidning",
+ name: "JSON binding",
binding: JSON,
body: `{"foo":"FOO"}`,
},
{
- name: "XML bidning",
+ name: "XML binding",
binding: XML,
body: `
@@ -36,6 +36,11 @@ func TestBindingBody(t *testing.T) {
binding: MsgPack,
body: msgPackBody(t),
},
+ {
+ name: "YAML binding",
+ binding: YAML,
+ body: `foo: FOO`,
+ },
} {
t.Logf("testing: %s", tt.name)
req := requestWithBody("POST", "/", tt.body)
diff --git a/binding/binding_test.go b/binding/binding_test.go
index 4e10c4c8..183d111f 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -190,6 +190,9 @@ func TestBindingDefault(t *testing.T) {
assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
+
+ assert.Equal(t, YAML, Default("POST", MIMEYAML))
+ assert.Equal(t, YAML, Default("PUT", MIMEYAML))
}
func TestBindingJSON(t *testing.T) {
@@ -473,6 +476,20 @@ func TestBindingXMLFail(t *testing.T) {
"", "")
}
+func TestBindingYAML(t *testing.T) {
+ testBodyBinding(t,
+ YAML, "yaml",
+ "/", "/",
+ `foo: bar`, `bar: foo`)
+}
+
+func TestBindingYAMLFail(t *testing.T) {
+ testBodyBindingFail(t,
+ YAML, "yaml",
+ "/", "/",
+ `foo:\nbar`, `bar: foo`)
+}
+
func createFormPostRequest() *http.Request {
req, _ := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
req.Header.Set("Content-Type", MIMEPOSTForm)
diff --git a/binding/protobuf.go b/binding/protobuf.go
index 540e9c1f..f9ece928 100644
--- a/binding/protobuf.go
+++ b/binding/protobuf.go
@@ -29,7 +29,7 @@ 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
+ // Here it's same to return validate(obj), but util now we can't add
// `binding:""` to the struct which automatically generate by gen-proto
return nil
// return validate(obj)
diff --git a/binding/yaml.go b/binding/yaml.go
new file mode 100644
index 00000000..a2d36d6a
--- /dev/null
+++ b/binding/yaml.go
@@ -0,0 +1,35 @@
+// Copyright 2018 Gin Core Team. 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 (
+ "bytes"
+ "io"
+ "net/http"
+
+ "gopkg.in/yaml.v2"
+)
+
+type yamlBinding struct{}
+
+func (yamlBinding) Name() string {
+ return "yaml"
+}
+
+func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
+ return decodeYAML(req.Body, obj)
+}
+
+func (yamlBinding) BindBody(body []byte, obj interface{}) error {
+ return decodeYAML(bytes.NewReader(body), obj)
+}
+
+func decodeYAML(r io.Reader, obj interface{}) error {
+ decoder := yaml.NewDecoder(r)
+ if err := decoder.Decode(obj); err != nil {
+ return err
+ }
+ return validate(obj)
+}
diff --git a/context.go b/context.go
index df064448..478e8c09 100644
--- a/context.go
+++ b/context.go
@@ -31,6 +31,7 @@ const (
MIMEPlain = binding.MIMEPlain
MIMEPOSTForm = binding.MIMEPOSTForm
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
+ MIMEYAML = binding.MIMEYAML
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
)
@@ -524,8 +525,13 @@ func (c *Context) BindQuery(obj interface{}) error {
return c.MustBindWith(obj, binding.Query)
}
+// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
+func (c *Context) BindYAML(obj interface{}) error {
+ return c.MustBindWith(obj, binding.YAML)
+}
+
// MustBindWith binds the passed struct pointer using the specified binding engine.
-// It will abort the request with HTTP 400 if any error ocurrs.
+// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
if err = c.ShouldBindWith(obj, b); err != nil {
@@ -563,6 +569,11 @@ func (c *Context) ShouldBindQuery(obj interface{}) error {
return c.ShouldBindWith(obj, binding.Query)
}
+// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
+func (c *Context) ShouldBindYAML(obj interface{}) error {
+ return c.ShouldBindWith(obj, binding.YAML)
+}
+
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
func (c *Context) ShouldBindUri(obj interface{}) error {
m := make(map[string][]string)
diff --git a/context_test.go b/context_test.go
index fb492e02..dced73fd 100644
--- a/context_test.go
+++ b/context_test.go
@@ -1380,6 +1380,23 @@ func TestContextBindWithQuery(t *testing.T) {
assert.Equal(t, 0, w.Body.Len())
}
+func TestContextBindWithYAML(t *testing.T) {
+ w := httptest.NewRecorder()
+ c, _ := CreateTestContext(w)
+
+ c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
+ c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+
+ var obj struct {
+ Foo string `yaml:"foo"`
+ Bar string `yaml:"bar"`
+ }
+ assert.NoError(t, c.BindYAML(&obj))
+ assert.Equal(t, "foo", obj.Bar)
+ assert.Equal(t, "bar", obj.Foo)
+ assert.Equal(t, 0, w.Body.Len())
+}
+
func TestContextBadAutoBind(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
@@ -1470,6 +1487,23 @@ func TestContextShouldBindWithQuery(t *testing.T) {
assert.Equal(t, 0, w.Body.Len())
}
+func TestContextShouldBindWithYAML(t *testing.T) {
+ w := httptest.NewRecorder()
+ c, _ := CreateTestContext(w)
+
+ c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
+ c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
+
+ var obj struct {
+ Foo string `yaml:"foo"`
+ Bar string `yaml:"bar"`
+ }
+ assert.NoError(t, c.ShouldBindYAML(&obj))
+ assert.Equal(t, "foo", obj.Bar)
+ assert.Equal(t, "bar", obj.Foo)
+ assert.Equal(t, 0, w.Body.Len())
+}
+
func TestContextBadAutoShouldBind(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
diff --git a/docs/how-to-build-an-effective-middleware.md b/docs/how-to-build-an-effective-middleware.md
index db04428c..568d5720 100644
--- a/docs/how-to-build-an-effective-middleware.md
+++ b/docs/how-to-build-an-effective-middleware.md
@@ -4,9 +4,9 @@
The middleware has two parts:
- - part one is what is executed once, when you initalize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime.
+ - part one is what is executed once, when you initialize your middleware. That's where you set up all the global objects, logicals etc. Everything that happens one per application lifetime.
- - part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler furnction.
+ - part two is what executes on every request. For example, a database middleware you simply inject your "global" database object into the context. Once it's inside the context, you can retrieve it from within other middlewares and your handler function.
```go
func funcName(params string) gin.HandlerFunc {
diff --git a/errors.go b/errors.go
index 477b9d58..ab13ca61 100644
--- a/errors.go
+++ b/errors.go
@@ -24,9 +24,10 @@ const (
ErrorTypePrivate ErrorType = 1 << 0
// ErrorTypePublic indicates a public error.
ErrorTypePublic ErrorType = 1 << 1
- // ErrorTypeAny indicates other any error.
+ // ErrorTypeAny indicates any other error.
ErrorTypeAny ErrorType = 1<<64 - 1
- ErrorTypeNu = 2
+ // ErrorTypeNu indicates any other error.
+ ErrorTypeNu = 2
)
// Error represents a error's specification.
@@ -52,6 +53,7 @@ func (msg *Error) SetMeta(data interface{}) *Error {
return msg
}
+// JSON creates a properly formated JSON
func (msg *Error) JSON() interface{} {
json := H{}
if msg.Meta != nil {
diff --git a/examples/grpc/gin/main.go b/examples/grpc/gin/main.go
index edc1ca9b..820e65a3 100644
--- a/examples/grpc/gin/main.go
+++ b/examples/grpc/gin/main.go
@@ -19,7 +19,7 @@ func main() {
defer conn.Close()
client := pb.NewGreeterClient(conn)
- // Set up a http setver.
+ // Set up a http server.
r := gin.Default()
r.GET("/rest/n/:name", func(c *gin.Context) {
name := c.Param("name")
diff --git a/examples/realtime-advanced/resources/room_login.templ.html b/examples/realtime-advanced/resources/room_login.templ.html
index 27dac387..905c012f 100644
--- a/examples/realtime-advanced/resources/room_login.templ.html
+++ b/examples/realtime-advanced/resources/room_login.templ.html
@@ -1,4 +1,4 @@
-
+
@@ -20,9 +20,9 @@
-
+
-