古老的榕树

Go 的表单绑定器

发表 2016-01-10 23:21 阅读(2043) 评论(0) 赞(0)
Go 做 Web 开发,也算成熟了,平时后台开发,需要一款得心应手的表单绑定器,表单绑定器能快捷把表单数据填充业务实体类,免去 request 里一个个获取数据,赋值给实体类,省去了大量的时间和代码。

目前发现两个比较好用的表单绑定器:
binding: https://github.com/mholt/binding
bind: https://github.com/robfig/bind

自己常用的是 binding,功能比较完善,人气也多一些;而 bind 人气少很多,几乎没有被人发现。要我说哪个好用,个人觉得 bind 直接些,因为 bind 使用起来很简单,纯粹奔着绑定表单用途,实体类 struct 不需要写额外代码,但代价是表单字段名和实体类属性名一致,在业务代码块,直接使用 bind 即可,节省了代码;返回object 错误;至于 binding 需要再实体类中添加一些 FieldMap 代码,返回错误是 json 格式的,功能更强大,可以自定义错误信息,可以验证字段,表单别名映射更灵活等等优势。

两者支持的数据类型都很丰富,基本全面涵盖了可能出现的类型。

bind 支持的数据类型如下:
 - bool
 - float32, float64
 - int, int8, int16, int32, int64
 - uint, uint8, uint16, uint32, uint64
 - string
 - struct
 - a pointer to any supported type
 - a slice of any supported type
 - time.Time
 - uploaded files (as io.Reader, io.ReadSeeker, *os.File, []byte, *multipart.FileHeader)

binding 支持的数据类型如下:
 - uint, *uint, []uint, uint8, *uint8, []uint8, uint16, *uint16, []uint16, uint32, *uint32,  - []uint32, uint64, *uint64, []uint64
 - int, *int, []int, int8, *int8, []int8, int16, *int16, []int16, int32, *int32, []int32, int64,  - *int64, []int64
 - float32, *float32, []float32, float64, *float64, []float64
 - bool, *bool, []bool
 - string, *string, []string
 - time.Time, *time.Time, []time.Time
 - multipart.FileHeader, []multipart.FileHeader

作者描述详尽不一样,其实 bind 也支持 Slices 类型


bind 的使用方法:
POST /accounts/:accountId/users/?op=UPDATE
 <form>
  <input name="user.Id">
  <input name="user.Name">
  <input name="user.Phones[0].Label">
  <input name="user.Phones[0].Number">
  <input name="user.Phones[1].Label">
  <input name="user.Phones[1].Number">
  <input name="user.Labels[]">
  <input name="user.Labels[]">
 </form>

 type Phone struct { Label, Number string }

 type User struct {
   Id     uint32
   Phones []Phone
   Labels []string
 }

 var (
   params = mux.Vars(req) // embedded URL args
   id     uint32
   op     string
   user   User
 )

 handleErrors(
   bind.Map(params).Field(&id, "accountId"),
   bind.Request(req).Field(&op, "op")
   bind.Request(req).Field(&user, "user"),
 )

相当简洁明了!!!!


binding 使用方法:


// First define a type to hold the data
// (If the data comes from JSON, see: http://mholt.github.io/json-to-go)
type ContactForm struct {
    User struct {
        ID int
    }
    Email   string
    Message string
    Created time.Time
}

// Then provide a field mapping (pointer receiver is vital)
func (cf *ContactForm) FieldMap(req *http.Request) binding.FieldMap {
    return binding.FieldMap{
        &cf.User.ID: "user_id",
        &cf.Email:   "email",
        &cf.Message: binding.Field{
            Form:     "message",
            Required: true,
        },
&cf.Created: binding.Field{
   Form:       "advert.Created",
   TimeFormat: "2006-01-02 15:04:05",
},
    }
}

// Now your handlers can stay clean and simple
func handler(resp http.ResponseWriter, req *http.Request) {
    contactForm := new(ContactForm)
    errs := binding.Bind(req, contactForm)
    if errs.Handle(resp) {
        return
    }
    fmt.Fprintf(resp, "From:    %d\n", contactForm.User.ID)
    fmt.Fprintf(resp, "Message: %s\n", contactForm.Message)
}
很明显 实体类需要定义 FieldMap 方法,做一个表单和实体类的桥接定义。

两个功能都比较齐全,上传文件也会支持。绑定时间的时候,bingding 需要注意时区的问题,必要时候需要指定 TimeFormat,如上Created的定义。而 bind 默认支持时间格式:"2006-01-02 15:04",""2006-01-02",如果需要时间秒,则需要指定:
func init() {
    bind.TimeFormats = append(bind.TimeFormats, "2006-01-02 15:04:05")
} 

-EOF-
Donate

如果文章对您有帮助,请使用手机支付宝扫描二维码,捐赠X元,作者离不开读者的支持!

0 条网友评论

哇~~~ 竟然还没有评论!

称呼*
邮箱*
内容*
验证码*
验证码 看不清换张