(转)Beego脱坑(八)表单数据验证

发布时间 2023-03-24 11:05:31作者: liujiacai

原文:https://blog.csdn.net/yang731227/article/details/82252016

title: Beego脱坑(八)表单数据验证

tags: go,beego

author : Clown95

表单就是用来收集用户的一些信息,既然是用户填写那么可能会出现错误填写错误的情况,比如说手机号码多一位或者少一位啊都有可能。更严重的是存在的一些恶意用户填写非法关键字来获取网站重要数据,比如使用sql注入。为了能够避免这些情况,我们就需要使用表单数据验证。

本章不仅会涉及到表单验证,还将会对之前的知识点进行一些融合,例如表单数据获取、数据解析到结构体、模板动态输出。

安装
beego为我们提供了validation这个库,它主要用于数据验证和错误收集,我们现在来安装它。

go get -u github.com/astaxie/beego/validation

实现
还记得我之前写的那个注册用户的表单吗?现在我们来对它改造一番。下面是我们表单的效果图:

 

 

 

现在我们需要实现:

验证所有的数据不能为空
验证用户名和密码长度大于等于6位
验证两次密码是否相等
检测年龄是否为数字
验证身份证、邮箱、手机信息是否合法


我们先实现表单模板:
validation.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>账号注册</title>
</head>
<body>
<form action="/validation" method="post" >
<table>
<tbody>
<tr>
<td>用户名</td>
<td>
<input type="text" name ="User" value=""> <span style="color: red;"> {{.VerifyUser}} </span>
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="password" name ="Pwd" value=""><span style="color: red;">{{.VerifyPwd}}</span>
</td>
</tr>
<tr>
<td>确认密码</td>
<td>
<input type="password" name ="CfmPwd" value=""><span style="color: red;">{{.VerifyCfm}}</span>
</td>
</tr>

<tr>
<td>真实姓名</td>
<td>
<input type="text" name ="RealName" value=""><span style="color: red;">{{.VerifyRealName}}</span>
</td>
</tr>
<tr>
<td>年龄</td>
<td>
<input type="text" name ="Age" value=""> <span style="color: red;">{{.VerifyAge}}</span>
</td>
</tr>
<tr>
<td>身份证</td>
<td>
<input type="text" name ="IdCard" value=""> <span style="color: red;">{{.VerifyIdCard}}</span>
</td>
</tr>
<tr>
<td>邮箱</td>
<td>
<input type="text" name ="Email" value=""> <span style="color: red;">{{.VerifyEmail}}</span>
</td>
</tr>
<tr>
<td>手机</td>
<td>
<input type="text" name ="Tel" value=""> <span style="color: red;">{{.VerifyTel}}</span>
</td>
</tr>
<tr>
<td>
<input type="submit" value="确认">
</td>
<td>
<input type="reset" value="重置">
</td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
具体代码:

package controllers

import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/validation"
)

type ValidationController struct {
beego.Controller
}

type User struct {
User string
Pwd string
RealName string
Age string //为了使用Numeric 我们先使用string类型
IdCard string
Email string
Tel string
}

func (this *ValidationController) Get() {
this.TplName = "validation.html"
}

func (this *ValidationController) Post() {
this.TplName = "validation.html"
var user User
//解析数据到结构体
if error := this.ParseForm(&user); error != nil {
this.Data["ParseFormErr"] = "数据解析到结构体错误"
} else {

varlid := validation.Validation{} //创建验证数据对象
//验证用户名不能为空且最小长度为6
userresult := varlid.Required(user.User, "User") //检测是否为空
if !userresult.Ok {
this.Data["VerifyUser"] = "用户名不能为空"
} else {
userresult = varlid.MinSize(user.User, 6, "User") //设置用户名最小长度为6
if !userresult.Ok {
this.Data["VerifyUser"] = "用户名最小长度为6位"
}
}
//验证密码不能为空且最小长度为6
pwdresult := varlid.Required(user.Pwd, "Pwd") //检测是否为空
if !pwdresult.Ok {
this.Data["VerifyPwd"] = "密码不能为空"
} else {
pwdresult = varlid.MinSize(user.Pwd, 6, "Pwd") //设置密码最小长度为6
if !pwdresult.Ok {
this.Data["VerifyPwd"] = "密码最小长度为6位"
}
}
//验证密码是否一样
cfmpwd := this.GetString("CfmPwd")
if cfmpwd != "" {
if cfmpwd != user.Pwd {
this.Data["VerifyCfm"] = "两次密码不一样"
}
} else {
this.Data["VerifyCfm"] = "确认密码不能为空"
}

//验证姓名不能为空
realnameresult := varlid.Required(user.RealName, "RealName")
if !realnameresult.Ok {
this.Data["VerifyRealName"] = "真实姓名不能为空"
}

//验证年龄是否为数字
ageresult := varlid.Numeric(user.Age, "Age")
if !ageresult.Ok {
this.Data["VerifyAge"] = "年龄只能是数字"
}

//验证身份证是否合法
idcardresult := varlid.Required(user.IdCard, "IdCard")
if !idcardresult.Ok {
this.Data["VerifyIdCard"] = "身份证不能为空"
} else {
idcardresult = varlid.Length(user.IdCard, 18, "IdCard") // 指定数据长度为18
if !idcardresult.Ok {
this.Data["VerifyIdCard"] = "身份证错误"
}
}

//验证邮箱
emailresult := varlid.Required(user.Email, "Email")
if !emailresult.Ok {
this.Data["VerifyEmail"] = "邮箱不能为空"
} else {
emailresult = varlid.Email(user.Email, "Email") //验证邮箱是否合法
if !emailresult.Ok {
this.Data["VerifyEmail"] = "邮箱格式错误"
}
}

//验证手机
telresult := varlid.Required(user.Tel, "Tel")
if !telresult.Ok {
this.Data["VerifyTel"] = "手机不能为空"
} else {
telresult = varlid.Mobile(user.Tel, "Tel") //验证手机是否合法
if !telresult.Ok {
this.Data["VerifyTel"] = "手机格式错误"
}
}

}

}

注册路由:

beego.Router("/validation",&controllers.ValidationController{})
第二种方式
我们还可以这样,代码简洁性大大增加:

func (this *ValidationController) Post() {
this.TplName = "validation.html"
var user User
//解析数据到结构体
if error := this.ParseForm(&user); error != nil {
this.Data["ParseFormErr"] = "数据解析到结构体错误"
} else {

valid := validation.Validation{} //创建验证数据对象
//验证用户名不能为空且最小长度为6
// Message 是自定义消息
valid.Required(user.User, "User").Message("用户名不能为空" )
valid.MinSize(user.User, 6,"User").Message("用户名最短为6位" )
valid.Required(user.Pwd, "Pwd").Message("密码不能为空" )
valid.MinSize(user.User, 6,"Pwd").Message("密码最短为6位" )
valid.Required(user.RealName, "RealName").Message("真实姓名不能为空" )
valid.Numeric(user.Age, "Age").Message("年龄只能为数字" )
valid.Required(user.IdCard, "IdCard").Message("身份证不能为空" )
valid.Length(user.IdCard, 18,"IdCard").Message("身份证格式非法" )
valid.Required(user.Email, "Email").Message("邮箱不能为空" )
valid.Email(user.Email, "Email").Message("邮箱格式非法" )
valid.Required(user.Tel, "Tel").Message("手机不能为空" )
valid.Mobile(user.Tel, "Tel").Message("手机格式非法" )

if valid.HasErrors() {
// 如果有错误信息,证明验证没通过
// 打印错误信息
for _, err := range valid.Errors {
data:= "Verify"+err.Key
this.Data[data] =err.Message
//log.Println(err.Key, err.Message)
}
}
}
}
我们简单的分析下上面的代码:

我们首先把表单数据解析到结构体
使用validation.Validation{} 创建验证数据对象。
调用Required方法来验证是否为空
调用MinSize方法来指定最小长度
调用Numeric方法来验证字符串内容为数字
调用Length方法来指定固定长度
调用Email方法来验证邮箱格式是否合法
调用Mobile方法来验证手机格式是否合法

使用StructTag
我们可以使用StructTag让代码更加简洁:

我们先修改User结构体,给它加上Tag

type User struct {
User string `valid:"Required;MinSize(6)"` //不为空且最小长度为6
Pwd string `valid:"Required;MinSize(6)"` //不为空且最小长度为6
RealName string `valid:"Required"` //不为空
Age string `valid:"Numeric"` //为数字字符串
IdCard string `valid:"Required;Length(18)"` //不为空且长度为18
Email string `valid:"Required;Email"` //不为空且格式为Email
Tel string `valid:"Required;Mobile"` //不为空且格式为mobile
}

控制器:


func (this *ValidationController) Post() {

var user User
//解析数据到结构体
if error := this.ParseForm(&user); error != nil {
this.Data["ParseFormErr"] = "数据解析到结构体错误"
} else {

this.TplName = "validation.html"
valid := validation.Validation{}

//自定义消息提示,官方默认的是英文,我们手动改成中文,具体内容可以查看 MessageTmpls 。
//下面这段代码,并不是必须的
var MessageTmpls = map[string]string{
"Required": "不能为空",
"MinSize": "最短长度为 %d",
"Length": "长度必须为 %d",
"Numeric": "必须是有效的数字",
"Email": "必须是有效的电子邮件地址",
"Mobile": "必须是有效的手机号码",
}
validation.SetDefaultMessage(MessageTmpls)
// 上面的代码

b, err := valid.Valid(&user)
if err != nil {
// 验证StructTag 是否正确
this.Data["ParseFormErr"] = err
}
if !b {
// 验证没通过 输出错误信息
//valid.Errors 的信息存储在预定义的MessageTmpls中

for _, err := range valid.Errors {
//log.Println(err.Field, err.Message)
data := "Verify" + err.Field
this.Data[data] = err.Field+err.Message
}
}
}
}

这里注意一下,上面所使用的 SetDefaultMessage 方法它可以帮我们重设错误信息,此函数文档中未介绍,但可在包 beego/validation/validators.go 中找到。

我们只是使用了部分验证函数,官方还提供了很多其他的验证,大部分方法的实现原理就是内部帮我们写好了正则表达式,并且根据这个正则帮我们进行验证。

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,其他类型都将不能通过验证
Alpha alpha字符,有效类型:string,其他类型都将不能通过验证
Numeric 数字,有效类型:string,其他类型都将不能通过验证
AlphaNumeric alpha 字符或数字,有效类型:string,其他类型都将不能通过验证
Match(pattern string) 正则匹配,有效类型:string,其他类型都将被转成字符串再匹配(fmt.Sprintf(“%v”, obj).Match)
AlphaDash alpha 字符或数字或横杠 -_,有效类型:string,其他类型都将不能通过验证
Email 邮箱格式,有效类型:string,其他类型都将不能通过验证
IP IP 格式,目前只支持 IPv4 格式验证,有效类型:string,其他类型都将不能通过验证
Base64 base64 编码,有效类型:string,其他类型都将不能通过验证
Mobile 手机号,有效类型:string,其他类型都将不能通过验证
Tel 固定电话号,有效类型:string,其他类型都将不能通过验证
Phone 手机号或固定电话号,有效类型:string,其他类型都将不能通过验证
ZipCode 邮政编码,有效类型:string,其他类型都将不能通过验证
————————————————