golang学习之gin(五):数据绑定及验证

一、数据绑定:

1. 数据绑定介绍:

Gin提供了两类绑定方法:

  • Must bind:
    • Methods:
      • Bind, BindJSON, BindXML, BindQuery, BindYAML
    • Behavior:
      • 这些方法属于MustBindWith的具体调用. 如果发生绑定错误, 则请求终止, 并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)响应状态码被设置为 400 并且Content-Type被设置为text/plain; charset=utf-8. 如果您在此之后尝试设置响应状态码, Gin会输出日志[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422. 如果您希望更好地控制绑定, 考虑使用ShouldBind等效方法.
  • Should bind:
    • Methods:
      • ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
    • Behavior:
      • 这些方法属于ShouldBindWith的具体调用. 如果发生绑定错误, Gin 会返回错误并由开发者处理错误和请求.

2. 数据绑定–Should bind:

2.1 ShouldBind:

1. 2├── chapter05 3│ └── bind_form.go 4├── main.go 5├── static 6├── template 7│ ├── chapter05 8│ │ └── user_add.html
  • ./main.go
1package main 2 3import ( 4 "gin_project/chapter05" 5 "github.com/gin-gonic/gin" 6) 7 8func main() { 9 engine := gin.Default() 10 // 注册模板 11 engine.LoadHTMLGlob("template/**/*") 12 // 注册静态文件 13 engine.Static("./static", "static") 14 15 // 注册路由 16 engine.GET("/to_bindform", chapter05.ToBindform) 17 engine.POST("/push_bindform", chapter05.DoBindform) 18 19 engine.GET("/push_bindquery", chapter05.DoBindquery) // http://localhost:9000/push_bindquery?name=zhangsan&age=110&addr=%E5%8C%97%E4%BA%AC 20 engine.POST("/push_bindajax", chapter05.DoBindajax) 21 22 engine.Run(":9000") 23}
  • ./chapter05/bind_form.go
1package chapter05 2 3import ( 4 "fmt" 5 "net/http" 6 "github.com/gin-gonic/gin" 7) 8 9type User struct { 10 Name string `form:"name" json:"name"` 11 Age int `form:"age" json:"age"` 12 Addr string `form:"addr" json:"addr"` 13} 14 15func ToBindform(ctx *gin.Context) { 16 ctx.HTML(http.StatusOK, "chapter05/user_add.html", nil) 17} 18 19// bind form data 20func DoBindform(ctx *gin.Context) { 21 var user User 22 err := ctx.ShouldBind(&user) 23 fmt.Println(user) 24 if err != nil { 25 ctx.String(http.StatusNotFound, "绑定form失败") 26 } else { 27 ctx.String(http.StatusOK, "绑定form成功") 28 } 29} 30 31// bind Query data 32func DoBindquery(ctx *gin.Context) { 33 var user User 34 err := ctx.ShouldBind(&user) 35 fmt.Println(user) 36 if err != nil { 37 ctx.String(http.StatusNotFound, "绑定query失败") 38 } else { 39 ctx.String(http.StatusOK, "绑定query成功") 40 } 41} 42 43// bind Ajax data 44func DoBindajax(ctx *gin.Context) { 45 var user User 46 err := ctx.ShouldBind(&user) 47 fmt.Println(user) 48 if err != nil { 49 ctx.String(http.StatusNotFound, "绑定ajax失败") 50 } else { 51 ctx.String(http.StatusOK, "绑定ajax成功") 52 } 53}
  • ./template/chapter05/user_add.html
1{{ define "chapter05/user_add.html" }} 2<!DOCTYPE html> 3<html lang="zh"> 4<head> 5 <title>post请求练习</title> 6 <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> 7 <style> .userForm { 8 width: 480px; 9 height: 360px; 10 margin: 20px 200px; 11 } 12 input { 13 margin: 5px 0; 14 15 } </style> 16</head> 17<body> 18 <div class="userForm"> 19 <h2>bind form data</h2> 20 <form action="/push_bindform" method="post"> 21 <p>姓名:<input type="text" name="name" id="name"></p> 22 <p>年龄:<input type="text" name="age" id="age"></p> 23 <p>地址:<input type="text" name="addr" id="addr"></p> 24 <p><input type="submit" value="提交form"></p> 25 <p><input type="button" value="提交ajax" id="sub-btn"></p> 26 </form> 27 </div> 28 29 <script> // 使用ajax提交json 30 var btn = document.querySelector("#sub-btn") 31 btn.onclick = function(ev) { 32 var name = document.getElementById("name").value; 33 var age = document.getElementById("age").value; 34 var addr = document.getElementById("addr").value; 35 console.log(name, age, addr); 36 $.ajax({ 37 url:"/push_bindajax", 38 type:"POST", 39 data: JSON.stringify({ 40 "name":name, 41 "age":Number(age), 42 "addr":addr 43 }), 44 contentType: "application/json", 45 dataType: "json", 46 success:function (data) { 47 alert(data["code"]); 48 alert(data["msg"]); 49 }, 50 fail:function (data) { 51 console.log(data); 52 } 53 }) 54 55 } </script> 56</body> 57</html> 58{{ end }}

2.2 ShouldBindWith

可以使用显式绑定声明绑定 multipart form:

ctx.ShouldBindWith(&form, binding.Form) 

或者简单地使用 ShouldBind 方法自动绑定

2.3 ShouldBindQuery等

ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML等函数只绑定对应格式的参数

2.4 ShouldBindUri:绑定 Uri:

1package main 2 3import ( 4 "gin_project/chapter05" 5 "github.com/gin-gonic/gin" 6) 7 8func main() { 9 engine := gin.Default() 10 // 注册模板 11 engine.LoadHTMLGlob("template/**/*") 12 // 注册静态文件 13 engine.Static("./static", "static") 14 15 // 注册路由 16 engine.GET("/push_bindURI/:name/:age/:addr", chapter05.DoBindURI) // http://localhost:9000/push_bindURI/zhangsan/19/%E5%8C%97%E4%BA%AC 17 18 engine.Run(":9000") 19}
1package chapter05 2 3import ( 4 "fmt" 5 "net/http" 6 "github.com/gin-gonic/gin" 7) 8 9type User struct { 10 Name string `form:"name" json:"name" uri:"name"` 11 Age int `form:"age" json:"age" uri:"age"` 12 Addr string `form:"addr" json:"addr" uri:"addr"` 13} 14... 15 16// bind URI data 17func DoBindURI(ctx *gin.Context) { 18 var user User 19 err := ctx.ShouldBindUri(&user) 20 fmt.Println(user) 21 if err != nil { 22 ctx.String(http.StatusNotFound, "绑定uri失败") 23 } else { 24 ctx.String(http.StatusOK, "绑定uri成功") 25 } 26}

3. 数据绑定–Must bind

3.1 Bind

可以绑定Form、QueryString、Json等

和ShouldBind的区别在于,ShouldBind没有绑定成功不报错,就是空值,Bind会报错

3.2 BindQuery等

BindJSON, BindXML, BindQuery, BindYAML等函数只绑定对应格式的参数

二、数据验证:

1. 使用:

go-playground/validator.v8进行验证
使用structTagbinding,如:binding:"required"
如果没有空值或者类型不匹配就会报错,重定向到400 (Bad Request)
错误信息:Key: 'Article.Title' Error:Field validation for 'Title' failed on the 'required' tag

1./ 2├── chapter05 3│ └── valid_data.go 4├── main.go 5├── static 6├── template 7│ ├── chapter05 8│ │ └── valid_data.html
  • ./main.go
1package main 2 3import ( 4 "gin_project/chapter05" 5 "github.com/gin-gonic/gin" 6) 7 8func main() { 9 engine := gin.Default() 10 // 注册模板 11 engine.LoadHTMLGlob("template/**/*") 12 // 注册静态文件 13 engine.Static("./static", "static") 14 15 // 注册路由 16 engine.GET("/to_valid", chapter05.ToValidData) 17 engine.POST("/do_valid", chapter05.DoValidData) 18 19 engine.Run(":9000") 20}
  • ./chapter05/valid_data.go
1package chapter05 2 3import ( 4 "fmt" 5 "net/http" 6 "github.com/gin-gonic/gin" 7) 8 9type Article struct { 10 Id int `form:"-"` 11 Title string `form:"title" binding:"required"` 12 Content string `form:"content"` 13 Desc string `form:"desc"` 14} 15 16func ToValidData(ctx *gin.Context) { 17 ctx.HTML(http.StatusOK, "chapter05/valid_data.html", nil) 18} 19 20func DoValidData(ctx *gin.Context) { 21 var article Article 22 err := ctx.ShouldBind(&article) 23 if err != nil { 24 fmt.Println(err) 25 ctx.JSON(http.StatusBadRequest, gin.H{ 26 "msg": "参数错误", 27 }) 28 } 29 fmt.Println(article) 30 31 ctx.String(http.StatusOK, "成功") 32}
  • ./template/chapter05/valid_data.html
1{{ define "chapter05/valid_data.html" }} 2<!DOCTYPE html> 3<html lang="cn"> 4<head> 5 <title>数据验证</title> 6 <style> .form { 7 width: 300px; 8 height: 300px; 9 margin: 100px auto; 10 } </style> 11</head> 12<body> 13 <div class="form"> 14 <form action="/do_valid" method="post"> 15 <p>title: <input type="text" name="title"></p> 16 <p>content: <input type="text" name="content"></p> 17 <p>desc: <input type="text" name="desc"></p> 18 <p><input type="submit" name="btn" value="提交"></p> 19 </form> 20 </div> 21</body> 22</html> 23{{ end }}

2. 其他验证器:

官方文档:https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags

注意:
多个验证器之间用英文输入法下的逗号(,)隔开, 并且是按照验证器的顺序执行的
如果希望在参数中包含逗号(即excludesall =,), 则需要使用UTF-8十六进制表示形式0x2C例如: validate:"excludesall=0x2C"

验证器说明示例
-忽略字段binding:"-"
required必填字段binding:“required”
min最小长度binding:“min=10”
max最大长度binding:“max=10”
structonly如果有嵌套,可以决定只验证结构体上的binding:“structonly”
omitempty省略空,如果为空,则不会继续验证该字段上其他的规则,只有不为空才会继续验证其他的
len长度binding:“len=10”
eq等于binding:“eq=10”
ne不等于binding:“ne=10”
gt大于binding:“gt=10”
gte大于等于binding:“gte=10”
lt小于binding:“lt=10”
lte小于等于binding:“lte=10”
eqfield等于其他字段的值Password string binding:"eqfield=ConfirmPassword"
nefield不等于其他字段的值
eqcsfield类似eqfield,它会验证相对于顶层结构提供的字段binding:"eqcsfield = InnerStructField.Field
necsfield
gtfield大于其他字段的值
gtefield
gtcsfield
gtecsfield
ltfield小于其他字段的值
ltefield
ltcsfield
ltecsfield
alpha字符串值仅包含字母字符
alphanum字符串值仅包含字母数字字符
numeric字符串值包含基本数字值。基本不包括指数等…
hexadecimal字符串值包含有效的十六进制
hexcolor验证字符串值包含有效的十六进制颜色, 包括井号(#)
rgb字符串值包含有效的rgb颜色
rgba字符串值包含有效的rgba颜色
HSL字符串值包含有效的hsl颜色
hsla字符串值包含有效的hsla颜色
email字符串值包含有效的电子邮件
url字符串值包含有效的网址,必须包含http://等
uri字符串值包含有效的uri. 它将接受golang请求uri接受的任何uri
base64字符串值包含有效的base64值
contains字符串值包含子字符串值, contains=@
containsany包含所有,containsany =!@#?
containsrune字符串值包含提供的符号 containsrune = @
excludes字符串值不包含子字符串值,excludes = @
excludeall排除所有
excluderune字符串值不包含提供的符号,excluderune = @
isbn国际标准书号,验证字符串值包含有效的isbn10或isbn13值
isbn10国际标准书号10, 验证字符串值包含有效的isbn10值
isbn13国际标准书号13, 字符串值包含有效的isbn13值
uuid字符串值包含有效的UUID
uuid3字符串值包含有效的版本3 UUID
uuid4字符串值包含有效的版本5 UUID
uuid5字符串值包含有效的版本5 UUID
ascii字符串值仅包含ASCII字符. 注意:如果字符串为空, 则验证为true
asciiprint字符串值仅包含可打印的ASCII字符. 注意: 如果字符串为空,则验证为true
multibyte字符串值包含一个或多个多字节字符。注意:如果字符串为空,则验证为true
datauri字符串值包含有效的DataURI。注意:这还将验证数据部分是有效的base64
latitude纬度,字符串值包含有效的纬度
longitude经度,字符串值包含有效的经度
ssn字符串值包含有效的美国社会安全号码
ip字符串值包含有效的IP地址
ipv4字符串值包含有效的v4 IP地址
ipv6字符串值包含有效的v6 IP地址
cidr字符串值包含有效的CIDR地址
cidrv4字符串值包含有效的CIDR地址
cidrv6字符串值包含有效的v6 CIDR地址
tcp_addr字符串值包含有效的可解析TCP地址
dive嵌套验证
1name [][]string `binding:"gt=0,dive,len=1,dive,required"` 2// gt = 0将应用于[] 3// len = 1将应用于[] string 4//必填项将应用于字符串 5 6name [][]string `binding:"gt=0,dive,dive,required"` 7// gt = 0 将应用于[] 8// []string 将保留验证 9//必填项将应用于字符串

注意:gt、gte、lt、lte等都可以用于时间的比较,后面不需要跟值,直接binding:“gt”,表示大于当前utc时间

3. 自定义验证器:

自定义验证器

3.1 安装:

go get github.com/go-playground/validator

3.2 定义验证器:

注意:必须为validator.Func类型

1var Len6Valid validator.Func = func(fl validator.FieldLevel) bool { 2 data := fl.Field().Interface().(string) 3 if len(data) > 6 { 4 fmt.Println("false") 5 return false 6 }else { 7 fmt.Println("true") 8 return true 9 } 10}

3.3 注册验证器:

在路由匹配前,main中即可

1if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 2 v.RegisterValidation("len_valid", valid.Len6Valid) 3}

3.4 结构体中使用:

这里必须binding中,而且名称为前面注册的字符串名称

1type Article struct { 2 Id int `form:"id"` 3 Title string `form:"title" binding:"required,len_valid"` 4 Desc string `form:"desc" binding:"required,len_valid"` 5}

4. beego中的验证器:

4.1 安装

go get github.com/astaxie/beego/validation

4.2 验证方法:

方法说明
Required不为空,即各个类型要求不为其零值
Min(min int)最小值,有效类型:int,其他类型都将不能通过验证
Max(max int)最大值,有效类型:int,其他类型都将不能通过验证
Range(min, max int)数值的范围,有效类型:int,他类型都将不能通过验证
MinSize(min int)最小长度,有效类型:string slice,其他类型都将不能通过验证
MaxSize(max int)最大长度,有效类型:string slice,其他类型都将不能通过验证
Length(length int)指定长度,有效类型:string slice,其他类型都将不能通过验证
Alphaalpha字符,有效类型:string,其他类型都将不能通过验证
Numeric数字,有效类型:string,其他类型都将不能通过验证
AlphaNumericalpha 字符或数字,有效类型:string,其他类型都将不能通过验证
Match(pattern string)正则匹配,有效类型:string,其他类型都将被转成字符串再匹配(fmt.Sprintf(“%v”, obj).Match)
AlphaDashalpha 字符或数字或横杠 -_,有效类型:string,其他类型都将不能通过验证
Email邮箱格式,有效类型:string,其他类型都将不能通过验证
IPIP 格式,目前只支持 IPv4 格式验证,有效类型:string,其他类型都将不能通过验证
Base64base64 编码,有效类型:string,其他类型都将不能通过验证
Mobile手机号,有效类型:string,其他类型都将不能通过验证
Tel固定电话号,有效类型:string,其他类型都将不能通过验证
Phone手机号或固定电话号,有效类型:string,其他类型都将不能通过验证
ZipCode邮政编码,有效类型:string,其他类型都将不能通过验证

4.3 通过 StructTag校验数据:

  • 验证函数写在 “valid” tag 的标签里
  • 各个验证规则之间用分号 “;” 分隔,分号后面可以有空格
  • 参数用括号 “()” 括起来,多个参数之间用逗号 “,” 分开,逗号后面可以有空格
  • 正则函数(Match)的匹配模式用两斜杠 “/” 括起来
  • 各个函数的结果的 key 值为字段名.验证函数名
1type LoginParams struct { 2 Name string valid:"Required" 3 Age int valid:"Required;MinSize(2)" 4 Addr string valid:"Required" 5 } 6 7func (l *LoginController) Post() { 8 valid := validation.Validation{} 9 // 解析到结构体 10 params := LoginParams{} 11 if err := l.ParseForm(&params); err != nil { 12 //handle error 13 return 14 } 15 16 //重写错误信息:validation.SetDefaultMessage(map) 17 var messages = map[string]string{ 18 "Required": "不能为空", 19 "MinSize": "最短长度为 %d", 20 "Length": "长度必须为 %d", 21 "Numeric": "必须是有效的数字", 22 "Email": "必须是有效的电子邮件地址", 23 "Mobile": "必须是有效的手机号码", 24 } 25 validation.SetDefaultMessage(messages) 26 27 // 校验 28 b, err := valid.Valid(&params) 29 // 验证StructTag 是否正确 30 if err != nil { 31 fmt.Println(err) 32 } 33 if !b { 34 // 验证没通过,则b为false 35 for _, err := range valid.Errors { 36 fmt.Println(err.Key, err.Message) 37 message := err.Key + err.Message 38 l.Ctx.WriteString(message) 39 } 40 } 41}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )