数据绑定与验证
中间件 binding 为 Macaron 实例 提供了请求数据绑定与验证的功能。
下载安装
- go get github.com/go-macaron/binding
使用示例
获取表单数据
假设您有一个联系人信息的表单,其中姓名和信息为必填字段,则我们可以使用如下结构来进行表示:
- type ContactForm struct {
- Name string `form:"name" binding:"Required"`
- Email string `form:"email"`
- Message string `form:"message" binding:"Required"`
- MailingAddress string `form:"mailing_address"`
- }
然后通过 Macaron 增加如下路由:
- m.Post("/contact/submit", binding.Bind(ContactForm{}), func(contact ContactForm) string {
- return fmt.Sprintf("Name: %s\nEmail: %s\nMessage: %s\nMailing Address: %v",
- contact.Name, contact.Email, contact.Message, contact.MailingAddress)
- })
搞定!函数 binding.Bind
会帮助您完成对必选字段的数据验证。
默认情况下,如果在验证过程中发生任何错误(例如:必填字段的值为空),binding 中间件就会直接向客户端返回错误信息,提前终止请求的处理。如果您不希望 binding 中间件自动终止请求的处理,则可以使用 binding.BindIgnErr
函数来忽略对错误的自动处理。
命名约定
默认情况下,form
标签的名称使用以下命名约定:
Name
->name
UnitPrice
->unit_price
也就是说,上面例子中的结构定义可以简化为如下代码:
- type ContactForm struct {
- Name string `binding:"Required"`
- Email string
- Message string `binding:"Required"`
- MailingAddress string
- }
超赞!有木有?
如果您想要自定义命名约定,可以通过 binding.SetNameMapper
函数来设置。该函数接受一个类型为 binding.NameMapper
的值作为参数。
获取 JSON 数据
将指定 form
标签的地方替换为 json
,就可以完成对 JSON 数据的绑定。
绑定到接口
如果您希望传递接口而不是一个具体的结构,则可以使用如下方法:
- m.Post("/contact/submit", binding.Bind(ContactForm{}, (*MyInterface)(nil)), func(contact MyInterface) {
- // ... 您接收到的值为一个接口
- })
处理器说明
原则上,每个处理器之间是相互独立的,但在特定情况下,它们之间会相互调用。
Bind
函数 binding.Bind
是一个便利性的高层封装,它能够自动识别表单类型并完成数据绑定与验证。
请求处理流程:
- 反序列化请求数据到结构
- 通过
binding.Validate
函数完成数据验证 - 如果您的结构实现了
binding.ErrorHandler
接口,则会调用相应的错误处理方法ErrorHandler.Error
;否则会使用默认的错误处理机制。
备注:
- 当使用默认的错误处理机制时,您的应用(队列后方的处理器)将根本不会意识到当前请求的存在。
- 头信息
Content-Type
是用于决定如何对请求数据进行反序列化的根本条件。
Form
函数 binding.Form
用于反序列化表单数据,可以是查询或 form-urlencoded
类型的请求。
请求处理流程:
- 反序列化请求数据到结构
- 通过
binding.Validate
函数完成数据验证
需要注意的是,该函数不具有默认错误处理机制。您可以通过获取类型为 binding.Errors
的参数来完成自定义错误处理。
MultipartForm 和文件上传
类似 binding.Form
,函数 binding.MultipartForm
同样是反序列化表单数据到结构。除此之外,它还能处理 enctype="multipart/form-data"
类型的 POST 请求。如果结构中包含类型为 *multipart.FileHeader
(或 []*multipart.FileHeader
)的字段,您可以直接从该字段读取客户端上传的文件。
请求处理流程:
- 反序列化请求数据到结构
- 通过
binding.Validate
函数完成数据验证
同样的,和函数 binding.Form
一样,该函数不具有默认错误处理机制,但您可以通过获取类型为 binding.Errors
的参数来完成自定义错误处理。
使用示例
- type UploadForm struct {
- Title string `form:"title"`
- TextUpload *multipart.FileHeader `form:"txtUpload"`
- }
- func main() {
- m := macaron.Classic()
- m.Post("/", binding.MultipartForm(UploadForm{}), uploadHandler(uf UploadForm) string {
- file, err := uf.TextUpload.Open()
- // ... 您可以在这里读取上传的文件内容
- })
- m.Run()
- }
Json
函数 binding.Json
反序列化 JSON 数据。
请求处理流程:
- 反序列化请求数据到结构
- 通过
binding.Validate
函数完成数据验证
与函数 binding.Form
,该函数不具有默认错误处理机制,但您可以通过获取类型为 binding.Errors
的参数来完成自定义错误处理。
Validate
函数 binding.Validate
接受一个结构并对它进行基本数据验证。如果该结构实现了 binding.Validator
接口,则会调用 Validator.Validate()
方法完成后续的数据验证。
验证规则
目前有一些内置的验证规则,通过格式为 binding:"<Name>"
的标签使用。
名称 | 说明 |
---|---|
OmitEmpty | 值为空时忽略后续验证 |
Required | 必须为相同类型的非零值 |
AlphaDash | 必须为半角英文字母、阿拉伯数字或 - |
AlphaDashDot | 必须为半角英文字母、阿拉伯数字、- 或 . |
Size(int) | 固定长度 |
MinSize(int) | 最小长度 |
MaxSize(int) | 最大长度 |
Range(int,int) | 取值范围(包含边界值) |
Email | 必须为邮箱地址 |
Url | 必须为 HTTP/HTTPS URL 地址 |
In(a,b,c,…) | 必须为数组的一个元素 |
NotIn(a,b,c,…) | 必须不是数组的元素 |
Include(string) | 必须包含 |
Exclude(string) | 必须不包含 |
Default(string) | 当字段为零值时设置默认值(当使用接口绑定时不能设置该规则) |
当需要使用多条规则时: binding:"Required;MinSize(10)"
。
自定义操作
自定义验证
如果您想要进行自定义的附加验证操作,您的结构可以通过实现接口 binding.Validator
来完成:
- func (cf ContactForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
- if strings.Contains(cf.Message, "Go needs generics") {
- errs = append(errors, binding.Error{
- FieldNames: []string{"message"},
- Classification: "ComplaintError",
- Message: "Go has generics. They're called interfaces.",
- })
- }
- return errs
- }
现在,任何包含信息 “Go needs generics” 的联系人表单都会报错。
自定义验证规则
当您觉得内置的验证规则不够时,可以通过函数 binding.AddParamRule
来增加自定义验证规则。该函数接受一个类型为 binding.ParamRule
的参数。
假设您需要验证字段的最小值:
- binding.AddParamRule(&binding.ParamRule{
- IsMatch: func(rule string) bool {
- return strings.HasPrefix(rule, "Min(")
- },
- IsValid: func(errs binding.Errors, rule, name string, v interface{}) (bool, binding.Errors) {
- num, ok := v.(int)
- if !ok {
- return false, errs
- }
- min, _ := strconv.Atoi(rule[4 : len(rule)-1])
- if num < min {
- errs.Add([]string{name}, "MinimumValue", "Value is too small")
- return false, errs
- }
- return true, errs
- },
- })
如果您的规则非常简单,也可以使用 binding.AddRule
,它接受类型为 binding.Rule
的参数:
- binding.AddRule(&binding.Rule{
- IsMatch: func(rule string) bool {
- return rule == "String"
- },
- IsValid: func(errs binding.Errors, name string, v interface{}) (bool, binding.Errors) {
- _, ok := v.(string)
- return ok, errs
- },
- })
自定义规则的应用发生在内置规则之后。
自定义错误处理
如果您即不想使用默认的错误处理机制,又希望 binding 中间件自动化地调用您的自定义错误处理,则可以通过实现接口 binding.ErrorHandler
来完成:
- func (cf ContactForm) Error(ctx *macaron.Context, errs binding.Errors) {
- // 自定义错误处理过程
- }
该操作发生在自定义验证规则被应用之后。