Data Binding¶
对 Web 开发来说,数据绑定无疑是非常重要的,它可以自动从请求上下文中获取数据然后反序列化到对象中,大大的简化了数据提取,让你专注于真正的业务开发。web.Context
提供了一个 Bind
方法来实现数据绑定,它本质上是通过调用注册在 web.Server
上的 Binder
对象来实现的。web 包中已经实现了一个了非常强大的 Binder
,并且默认就会注册到 web.Server
上,所以你不需要任何配置就可以使用。
user := &User{}
err := ctx.Bind(user)
非常简单,不是吗?接下来我们详细了解下内置 Binder
的原理与使用场景。
Bind JSON¶
当请求的 Context-Type 为 application/json 时,Binder
会把请求内容用 JSON 方式进行反序列化。标准库中的 JSON 反序列化规则完全适用于这里。
Bind XML¶
当请求的 Context-Type 为 application/xml 时,Binder
会把请求内容用 XML 方式进行反序列化。标准库中的 XML 反序列化规则完全适用于这里。
Bind parameters¶
除了 JSON 和 XML 之外,其它数据绑定需要依赖 bind
标签,它的格式为:[name],[name=type1],[name=type2]...
,其中有效的类型为 path/query/form/cookie/header,当 type
省略时,相当于同时指定了 query/form 类型。
让我们结合一个简单实例来解释下。
type TestUser struct {
Name string `json:"name" xml:"name" bind:"name,name1=path,name2=cookie"`
}
上面 Name
字段上定义的 bind
标签表示此字段可以绑定到 query 或 form 的 name 参数,或者 path 的 name1 参数,或者 cookie 的 name2 参数,所以下面这三个请求都可以成功绑定:
Method | URL | Route | Query | Body | Cookie |
---|---|---|---|---|---|
GET | /user | /user | name=foobar | ||
POST | /user | /user | name=foobar | ||
GET | /user/foobar | /user/:name1 | |||
GET | /user | /user | name2=foobar |
Bind file¶
对于上传的文件,内置的 Binder
提供了更为丰富的绑定策略。假设客户端上传一个头像图片,对应的表单字段名为 avatar,我们希望能自动把文件信息绑定到对象上,只需要定义下面的 struct
即可。
type Avatar struct {
Data []byte `bind:"avatar=file"`
Name string `bind:"avatar=file.name"`
Size int32 `bind:"avatar=file.size"`
}
Custom type support¶
内置的 Binder
支持几乎所有基本类型以及 time.Time 和 time.Duration 类型,如果需要支持自定义类型,只需要让类型实现 web.Unmarshaler
接口即可。
// Unmarshaler is the interface implemented by types that can unmarshal a param to themselves.
type Unmarshaler interface {
// Unmarshal converts a param value to type.
Unmarshal(param interface{}) error
}
下面的 FailMode
自定义类型演示了如何实现此接口。
type FailMode int
func (f *FailMode) Unmarshal(i interface{}) error {
if s, ok := i.(string); ok {
switch strings.ToLower(strings.TrimSpace(s)) {
case "over":
*f = FailOver
return nil
case "try":
*f = FailTry
return nil
case "fast", "":
*f = FailFast
return nil
}
}
return errors.Format("can't convert %v to FailMode", i)
}
Custom binder¶
Binder
是一个接口,它只有一个 Bind
方法,所以如果你不喜欢 auxo 中默认的实现,完全可以自己实现一个来替换它。
type YourBinder struct {}
// Bind implements `web.Binder` interface.
func (b *YourBinder) Bind(ctx Context, i interface{}) (err error) {
// your code...
}
func main() {
s := web.Auto()
s.Binder = &YourBinder{}
}
Data Validation¶
你可能已经发现 Context.Bind
方法其实还有一个布尔型的可选参数 validate
,这个参数可以让你在成功绑定数据后自动执行数据校验。
err := ctx.Bind(i, true)
跟 Binder
不同,创建 web.Server
实例时默认并没有注册任何验证器,所以你必须先设置 Validator
才能使用数据校验,否则将会触发一个运行时错误。auxo 中已经提供了一个完善的 Validator
实现,它就在 data/valid 包中,你可以这样来使用它:
s := web.Auto()
s.Validator = &valid.Validator{Tag: "valid"}
auxo 中内置验证器和验证规则的详细使用说明请查看 valid 包的相关文档。