|
| 1 | ++++ |
| 2 | +title = "Binding Request Data" |
| 3 | +description = "Binding request data" |
| 4 | +[menu.main] |
| 5 | + name = "Binding" |
| 6 | + parent = "guide" |
| 7 | ++++ |
| 8 | + |
| 9 | +## Bind using struct tags |
| 10 | + |
| 11 | +Echo provides following method to bind data from different sources (path params, query params, request body) to structure using |
| 12 | +`Context#Bind(i interface{})` method. |
| 13 | +The default binder supports decoding application/json, application/xml and |
| 14 | +application/x-www-form-urlencoded data based on the Content-Type header. |
| 15 | + |
| 16 | +In the struct definitions each field can be tagged to restrict binding to specific source. |
| 17 | + |
| 18 | +* `query` - source is request query parameters. |
| 19 | +* `param` - source is route path parameter. |
| 20 | +* `form` - source is form. Values are taken from query and request body. Uses Go standard library form parsing. |
| 21 | +* `json` - source is request body. Uses Go [json](https://golang.org/pkg/encoding/json/) package fo unmarshalling. |
| 22 | +* `xml` - source is request body. Uses Go [xml](https://golang.org/pkg/encoding/xml/) package fo unmarshalling. |
| 23 | + |
| 24 | +```go |
| 25 | +type User struct { |
| 26 | + ID string `path:"id" query:"id" form:"id" json:"id" xml:"id"` |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +Request data is binded to the struct in given order: |
| 31 | + |
| 32 | +1. Path parameters |
| 33 | +2. Query parameters (only for GET/DELETE methods) |
| 34 | +3. Request body |
| 35 | + |
| 36 | +Notes: |
| 37 | + |
| 38 | +* For `query`, `param`, `form` **only** fields **with** tags are bound. |
| 39 | +* For `json` and `xml` can bind to *public* fields without tags but this is by their standard library implementation. |
| 40 | +* Each step can overwrite binded fields from the previous step. This means if your json request has query param |
| 41 | + `&name=query` and body is `{"name": "body"}` then the result will be `User{Name: "body"}`. |
| 42 | +* To avoid security flaws try to avoid passing binded structs directly to other methods if |
| 43 | + these structs contain fields that should not be bindable. It is advisable to have separate struct for binding and map it |
| 44 | + explicitly to your business struct. Consider what will happen if your binded struct has public |
| 45 | + field `IsAdmin bool` and request body would contain `{IsAdmin: true, Name: "hacker"}`. |
| 46 | +* When binding forms take note that Echo implementation uses standard library form parsing which parses form data |
| 47 | + from BOTH URL and BODY if content type is not MIMEMultipartForm. See documentation for [non-MIMEMultipartForm](https://golang.org/pkg/net/http/#Request.ParseForm) |
| 48 | + and [MIMEMultipartForm](https://golang.org/pkg/net/http/#Request.ParseMultipartForm) |
| 49 | +* To bind data only from request body use following code |
| 50 | + ```go |
| 51 | + if err := (&DefaultBinder{}).BindBody(c, &payload); err != nil { |
| 52 | + return err |
| 53 | + } |
| 54 | + ``` |
| 55 | +* To bind data only from query parameters use following code |
| 56 | + ```go |
| 57 | + if err := (&DefaultBinder{}).BindQueryParams(c, &payload); err != nil { |
| 58 | + return err |
| 59 | + } |
| 60 | + ``` |
| 61 | +* To bind data only from path parameters use following code |
| 62 | + ```go |
| 63 | + if err := (&DefaultBinder{}).BindPathParams(c, &payload); err != nil { |
| 64 | + return err |
| 65 | + } |
| 66 | + ``` |
| 67 | + |
| 68 | +### Example |
| 69 | + |
| 70 | +Example below binds the request payload into `User` struct based on tags: |
| 71 | + |
| 72 | +```go |
| 73 | +// User |
| 74 | +type User struct { |
| 75 | + Name string `json:"name" form:"name" query:"name"` |
| 76 | + Email string `json:"email" form:"email" query:"email"` |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +```go |
| 81 | +e.POST("/users", func(c echo.Context) (err error) { |
| 82 | + u := new(User) |
| 83 | + if err = c.Bind(u); err != nil { |
| 84 | + return |
| 85 | + } |
| 86 | + // To avoid security flaws try to avoid passing binded structs directly to other methods |
| 87 | + // if these structs contain fields that should not be bindable. |
| 88 | + user := UserDTO{ |
| 89 | + Name: u.Name, |
| 90 | + Email: u.Email, |
| 91 | + IsAdmin: false // because you could accidentally expose fields that should not be bind |
| 92 | + } |
| 93 | + executeSomeBusinessLogic(user) |
| 94 | + |
| 95 | + return c.JSON(http.StatusOK, u) |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | +### JSON Data |
| 100 | + |
| 101 | +```sh |
| 102 | +curl -X POST http://localhost:1323/users \ |
| 103 | + -H 'Content-Type: application/json' \ |
| 104 | + -d '{"name":"Joe","email":"joe@labstack"}' |
| 105 | +``` |
| 106 | + |
| 107 | +### Form Data |
| 108 | + |
| 109 | +```sh |
| 110 | +curl -X POST http://localhost:1323/users \ |
| 111 | + -d 'name=Joe' \ |
| 112 | + |
| 113 | +``` |
| 114 | + |
| 115 | +### Query Parameters |
| 116 | + |
| 117 | +```sh |
| 118 | +curl -X GET http://localhost:1323/users \?name \=Joe \&email \=[email protected] |
| 119 | +``` |
| 120 | + |
| 121 | +## Fast binding with dedicated helpers |
| 122 | + |
| 123 | +For binding data found in a request a handful of helper functions are provided. This will allow binding of query parameters, path parameters or data found in the body like forms or JSON data. |
| 124 | + |
| 125 | +Following functions provide a handful of methods for binding to Go native types from request query or path parameters. These binders offer a fluent syntax and can be chained to configure, execute binding and handle errors. |
| 126 | + |
| 127 | +* `echo.QueryParamsBinder(c)` - binds query parameters (source URL) |
| 128 | +* `echo.PathParamsBinder(c)` - binds path parameters (source URL) |
| 129 | +* `echo.FormFieldBinder(c)` - binds form fields (source URL + body). See also [Request.ParseForm](https://golang.org/pkg/net/http/#Request.ParseForm). |
| 130 | + |
| 131 | +A binder is usually completed by `BindError()` or `BindErrors()` which returns errors if binding fails. |
| 132 | +With `FailFast()` the binder can be configured stop binding on the first error or continue binding for |
| 133 | +the binder call chain. Fail fast is enabled by default and should be disabled when using `BindErrors()`. |
| 134 | + |
| 135 | +`BindError()` returns the first bind error from binder and resets all errors in this binder. |
| 136 | +`BindErrors()` returns all bind errors from binder and resets errors in binder. |
| 137 | + |
| 138 | +```go |
| 139 | +// url = "/api/search?active=true&id=1&id=2&id=3&length=25" |
| 140 | +var opts struct { |
| 141 | + IDs []int64 |
| 142 | + Active bool |
| 143 | +} |
| 144 | +length := int64(50) // default length is 50 |
| 145 | + |
| 146 | +// creates query params binder that stops binding at first error |
| 147 | +err := echo.QueryParamsBinder(c). |
| 148 | + Int64("length", &length). |
| 149 | + Int64s("ids", &opts.IDs). |
| 150 | + Bool("active", &opts.Active). |
| 151 | + BindError() // returns first binding error |
| 152 | +``` |
| 153 | + |
| 154 | +### Supported types |
| 155 | + |
| 156 | +Types that are supported: |
| 157 | + |
| 158 | +* bool |
| 159 | +* float32 |
| 160 | +* float64 |
| 161 | +* int |
| 162 | +* int8 |
| 163 | +* int16 |
| 164 | +* int32 |
| 165 | +* int64 |
| 166 | +* uint |
| 167 | +* uint8/byte (does not support `bytes()`. Use BindUnmarshaler/CustomFunc to convert value from base64 etc to []byte{}) |
| 168 | +* uint16 |
| 169 | +* uint32 |
| 170 | +* uint64 |
| 171 | +* string |
| 172 | +* time |
| 173 | +* duration |
| 174 | +* BindUnmarshaler() interface |
| 175 | +* UnixTime() - converts unix time (integer) to time.Time |
| 176 | +* UnixTimeNano() - converts unix time with nano second precision (integer) to time.Time |
| 177 | +* CustomFunc() - callback function for your custom conversion logic |
| 178 | + |
| 179 | +For every supported type there are following methods: |
| 180 | + |
| 181 | +* `<Type>("param", &destination)` - if parameter value exists then binds it to given destination of that type i.e `Int64(...)`. |
| 182 | +* `Must<Type>("param", &destination)` - parameter value is required to exist, binds it to given destination of that type i.e `MustInt64(...)`. |
| 183 | +* `<Type>s("param", &destination)` - (for slices) if parameter values exists then binds it to given destination of that type i.e `Int64s(...)`. |
| 184 | +* `Must<Type>s("param", &destination)` - (for slices) parameter value is required to exist, binds it to given destination of that type i.e `MustInt64s(...)`. |
| 185 | + |
| 186 | +for some slice types `BindWithDelimiter("param", &dest, ",")` supports splitting parameter values before type conversion is done. For example URL `/api/search?id=1,2,3&id=1` can be bind to `[]int64{1,2,3,1}` |
| 187 | + |
| 188 | +## Custom Binder |
| 189 | + |
| 190 | +Custom binder can be registered using `Echo#Binder`. |
| 191 | + |
| 192 | +```go |
| 193 | +type CustomBinder struct {} |
| 194 | + |
| 195 | +func (cb *CustomBinder) Bind(i interface{}, c echo.Context) (err error) { |
| 196 | + // You may use default binder |
| 197 | + db := new(echo.DefaultBinder) |
| 198 | + if err = db.Bind(i, c); err != echo.ErrUnsupportedMediaType { |
| 199 | + return |
| 200 | + } |
| 201 | + |
| 202 | + // Define your custom implementation here |
| 203 | + return |
| 204 | +} |
| 205 | +``` |
0 commit comments