Skip to content

Commit

Permalink
docs(readme): Switch examples to use ShouldBind methods
Browse files Browse the repository at this point in the history
Add section for bind methods types, explain difference in behavior.
Switch all `c.Bind` examples to use `c.ShouldBind`.
  • Loading branch information
sudo-suhas committed Aug 16, 2017
1 parent 469c5cd commit e04297d
Showing 1 changed file with 57 additions and 23 deletions.
80 changes: 57 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ $ go run example.go

## Benchmarks

Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)

[See all benchmarks](/BENCHMARKS.md)

Expand Down Expand Up @@ -74,10 +74,10 @@ BenchmarkTigerTonic_GithubAll | 1000 | 1439483 | 239104
BenchmarkTraffic_GithubAll | 100 | 11383067 | 2659329 | 21848
BenchmarkVulcan_GithubAll | 5000 | 394253 | 19894 | 609

(1): Total Repetitions achieved in constant time, higher means more confident result
(2): Single Repetition Duration (ns/op), lower is better
(3): Heap Memory (B/op), lower is better
(4): Average Allocations per Repetition (allocs/op), lower is better
(1): Total Repetitions achieved in constant time, higher means more confident result
(2): Single Repetition Duration (ns/op), lower is better
(3): Heap Memory (B/op), lower is better
(4): Average Allocations per Repetition (allocs/op), lower is better

## Gin v1. stable

Expand Down Expand Up @@ -281,10 +281,10 @@ func main() {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)

// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
// c.SaveUploadedFile(file, dst)

c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
Expand Down Expand Up @@ -313,9 +313,9 @@ func main() {

for _, file := range files {
log.Println(file.Filename)

// Upload the file to specific dst.
// c.SaveUploadedFile(file, dst)
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
Expand Down Expand Up @@ -416,9 +416,17 @@ Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/valid

Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.

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 BindWith.
Also, Gin provides two sets of methods for binding:
- **Type** - Must bind
- **Methods** - `Bind`, `BindJSON`, `BindQuery`
- **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`, `ShouldBindQuery`
- **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.

You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, the current request will fail with an error.
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`.

You can also specify that specific fields are required. If a field is decorated with `binding:"required"` and has a empty value when binding, an error will be returned.

```go
// Binding from JSON
Expand All @@ -433,25 +441,29 @@ func main() {
// Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", func(c *gin.Context) {
var json Login
if c.BindJSON(&json) == nil {
if err = c.ShouldBindJSON(&json); err == nil {
if json.User == "manu" && json.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})

// Example for binding a HTML form (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// This will infer what binder to use depending on the content-type header.
if c.Bind(&form) == nil {
if err := c.ShouldBind(&form); err == nil {
if form.User == "manu" && form.Password == "123" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})

Expand All @@ -460,9 +472,31 @@ func main() {
}
```

**Sample request**
```shell
$ curl -v -X POST \
http://localhost:8080/loginJSON \
-H 'content-type: application/json' \
-d '{ "user": "manu" }'
> POST /loginJSON HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
> content-type: application/json
> Content-Length: 18
>
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Fri, 04 Aug 2017 03:51:31 GMT
< Content-Length: 100
<
{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
```

### Only Bind Query String

`BindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).
`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).

```go
package main
Expand All @@ -486,7 +520,7 @@ func main() {

func startPage(c *gin.Context) {
var person Person
if c.BindQuery(&person) == nil {
if c.ShouldBindQuery(&person) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
Expand Down Expand Up @@ -522,7 +556,7 @@ func startPage(c *gin.Context) {
// If `GET`, only `Form` binding engine (`query`) used.
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
if c.Bind(&person) == nil {
if c.ShouldBind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
}
Expand All @@ -548,7 +582,7 @@ type myForm struct {

func formHandler(c *gin.Context) {
var fakeForm myForm
c.Bind(&fakeForm)
c.ShouldBind(&fakeForm)
c.JSON(200, gin.H{"color": fakeForm.Colors})
}

Expand Down Expand Up @@ -595,11 +629,11 @@ func main() {
router := gin.Default()
router.POST("/login", func(c *gin.Context) {
// you can bind multipart form with explicit binding declaration:
// c.MustBindWith(&form, binding.Form)
// or you can simply use autobinding with Bind method:
// c.ShouldBindWith(&form, binding.Form)
// or you can simply use autobinding with ShouldBind method:
var form LoginForm
// in this case proper binding will be automatically selected
if c.Bind(&form) == nil {
if c.ShouldBind(&form) == nil {
if form.User == "user" && form.Password == "password" {
c.JSON(200, gin.H{"status": "you are logged in"})
} else {
Expand Down Expand Up @@ -676,7 +710,7 @@ func main() {
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```
```

### Serving static files

Expand Down Expand Up @@ -787,7 +821,7 @@ You may use custom delims
r := gin.Default()
r.Delims("{[{", "}]}")
r.LoadHTMLGlob("/path/to/templates"))
```
```

#### Custom Template Funcs

Expand Down

0 comments on commit e04297d

Please sign in to comment.