Request Parameters - Auto Bind
This document provides insights into aah’s Request Parameters auto parse and bind capabilities. So that you can take full advantage of them.
aah provides two ways to access your request parameters:
- Since v0.8.0 Auto Parse and Bind, this should be the preferred method.
- Use
ctx.Req.*
methods to get values.
Note: Auto Parse and Bind prevents the XSS attacks by sanitizing values. It is highly recommended to use.
Auto Parse and Bind
aah provides very flexible way to parse and bind request values into appropriate Go data types. It supports following:
- Bind any
Path
,Form
,Query
into controller action parameters, examples - Bind
JSON
orXML
request body intostruct
, examples - Bind any
Path
,Form
,Query
into controller actionstruct
fields, examples - Bind any
Path
,Form
,Query
into nestedstruct
following.
notation convention, examples - Bind supports bind of pointer and non-pointer, examples
- And you can also do combinations of above options
- You can added your own custom Value Parser by Type
Parse and Bind Priority
request.auto_bind.*
configuration is used for auto parse and bind. refer to config.
Supported Data Types
Binding of both pointer and non-pointer is supported.
- int, int8, int16, int32, int64
- uint, uint8, uint16, uint32, uint64
- float32, float64
- string
- bool - 1, t, T, true, TRUE, True, on, On will result as
true
- slice -
[]<type>
and[]*<type>
supported time.Time
- gets parsed based onformat.time = [...]
config from aah.confstruct
types- Type aliases of built-in types
- Custom types and aliases add your own value parser
Note:
- Any auto parse error will result into
400 Bad Request
, error details gets logged. - Multipart file binding is
intentionally not supported
by auto bind. It is mainly to use resources effectively. Instead aah provides dedicated method calledctx.Req.SaveFile
for convenience to save uploaded multipart-form files into disk easily.
Auto Parse and Bind Example Snippets
Following examples provides an idea to get started quickly with auto parse and bind feature. Let’s start with simple one.
Go with your creativity and use case to exploit the auto parse and bind feature.
Getting Pagination Values
// Let's say we have `/v1/products`
// Request is `/v1/products?page=3&count=25&sort=desc`
func (c *ProductController) Products(page, count int, sort string) {
c.Log().Info("Page No: ", page)
c.Log().Info("Count Per Page: ", count)
c.Log().Info("Sort Order: ", sort)
// ...
}
Getting Pagination Values into struct
// Tip: You can bind any `Path`, `Form`, `Query` fields into controller action `struct` fields.
// models package has this struct
// Pagination holds request pagination values.
type Pagination struct {
No int `bind:"page"`
Count int `bind:"count"`
}
// Let's say we have `/v1/products`
// It receives GET request with `/v1/products?page=3&count=25`
func (c *ProductController) Products(pagination *models.Pagination) {
c.Log().Info("Page No: ", pagination.No)
c.Log().Info("Count Per Page: ", pagination.Count)
// ...
}
Getting JSON Request Body into struct
Same principle is applicable for XML
content-type too.
// models package has this struct
// Product struct holds product fields.
type Product struct {
ID string `json:"id"`
Name string `json:"name"`
Title string `json:"title"`
IsActive bool `json:"is_active"`
Categories []string `json:"categories"`
}
// Let's say we have `/v1/products`
// It receives POST request to create product as JSON payload
func (c *ProductController) Products(product *models.Product) {
c.Log().Infof("%+v\n", product)
// ...
}
Getting Values into struct
and nested struct
Here we’re gonna use the user profile fields. Focus on how form fields residence.*
and shipping.*
are mapped into struct fields with .
notation.
// models package has this struct
// Address struct holds address information
type Address struct {
Address1 string `bind:"address1"`
Address2 string `bind:"address2"`
City string `bind:"city"`
ZipCode string `bind:"zip_code"`
}
// User struct holds user profile info
type User struct {
FirstName string `bind:"first_name"`
LastName string `bind:"last_name"`
Email string `bind:"email"`
ResidenceAddress *Address `bind:"residence"`
ShippingAddress *Address `bind:"shipping"`
}
// Request Information
// Method: POST
// Content-Type: application/x-www-form-urlencoded
// Form data:
// first_name=User Firstname
// last_name=User Lastname
// email=email@email.com
// residence.address1=Residence Address 1
// residence.address2=Residence Address 2
// residence.city=Residence City
// residence.zip_code=10002
// shipping.address1=Shipping Address 1
// shipping.address2=Shipping Address 2
// shipping.city=Shipping City
// shipping.zip_code=10001
// Let's say we have `/user/profile`
// It receives POST Form request to store user information.
func (c *UserController) Profile(user *models.User) {
c.Log().Infof("%+v\n", user)
// ...
}
Adding Custom Value Parser by Type
Implement your value parser per interface valpar.Parser
.
type Parser func(key string, typ reflect.Type, params url.Values) (reflect.Value, error)
Then add your parser into aah as follows:
func init() {
if err := aah.App().AddValueParser(reflect.TypeOf(CustomType{}), customParser); err != nil {
aah.App().Log().Error(err)
}
}