这是我参与更文挑战的第4天,大家好,我是作曲家种太阳
上一篇讲到了Zap日志管理和路由初始化,相信你对gin已近有了初步的认知.
这一篇我们讲下如何校验前端发送过来的数据,校验器的中文翻译,并编写一个自定义的校验器
1. 介绍
字段校验是后端比较重要的环节,虽然前端的form表单做了校验,但是后端为了安全性和避免数据库的
脏数据,
我们必须要做字段校验
字段校验我们虽然可以编写函数,自己封装校验逻辑,但是
结构体的tag
已近有一些基础校验功能,再加上可以自定义校验器,代码优雅效率十分高!
为了让你较为轻松的理解请求流程,画了一张数据流向图,了解下
整个数据流向走势
2. 快速实现最基础的字段校验
(1).定义校验结构体
在 forms/user.go 编写
package forms
type PasswordLoginForm struct {
PassWord string `form:"password" json:"password" binding:"required,min=3,max=20"`
Username string `form:"name" json:"name" binding:"required"`
结构体中的tag是自带一些校验的参数的,但是都是比较基础的,如果做一些严格的校验,需要做自定义校验器,后面我们会讲到.
(2).编写controller
在 controller/user.go 编写
package controller
import (
"github.com/fatih/color"
"github.com/gin-gonic/gin"
"go_gin/forms"
"net/http"
func PasswordLogin(c *gin.Context) {
PasswordLoginForm := forms.PasswordLoginForm{
if err := c.ShouldBind(&PasswordLoginForm); err != nil {
color.Blue(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"err": err.Error(),
return
c.JSON(http.StatusOK, gin.H{
"msg": "sussess",
(3).添加router路由
在 router/user.go中加一段路由设置
UserRouter.POST("login", controller.PasswordLogin)
(4).验证校验有效性
参数正确验证:
参数错误时验证:
name字段没有输入情况下
ps:能验证到这里,说明你以上的步骤都成功了,但是这里留了一个坑,就是验证的错误返回字段是英文的,我们下一步需要给验证器加上中文翻译转换器
3. 添加字段校验的中文转换器
1).安装validator,翻译器,中文
// validator校验器
go get github.com/go-playground/validator/v10
// 翻译器
go get github.com/go-playground/universal-translator
// 中文包
go get github.com/go-playground/locales/zh
// 英文包
go get github.com/go-playground/locales/en
(2).定义全局翻译器实例变量
在 global/globalVar中加上 变量
Trans ut.Translator
(3).编写翻译器
在 initialize/validator中
package initialize
import (
"fmt"
"github.com/fatih/color"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"go_gin/global"
"reflect"
"strings"
func InitTrans(locale string) (err error) {
color.Red("test111111111111")
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterTagNameFunc(func(fld reflect.StructField) string {
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
if name == "-" {
return ""
return name
zhT := zh.New()
enT := en.New()
uni := ut.New(enT, zhT, enT)
global.Trans, ok = uni.GetTranslator(locale)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", locale)
switch locale {
case "en":
_ = en_translations.RegisterDefaultTranslations(v, global.Trans)
case "zh":
_ = zh_translations.RegisterDefaultTranslations(v, global.Trans)
default:
_ = en_translations.RegisterDefaultTranslations(v, global.Trans)
return
return
ps:大致流程是给InitTrans传递一个参数,判断加载什么语言包,然后获取到语言包赋值给全局翻译器
(3).编写字段校验异常函数
封装一个统一处理字段异常的函数
在 utils/validator 编写
package utils
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"go_gin/global"
"net/http"
"strings"
func HandleValidatorError(c *gin.Context, err error) {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
c.JSON(http.StatusBadRequest, gin.H{
"error": removeTopStruct(errs.Translate(global.Trans)),
return
func removeTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fileds {
rsp[field[strings.Index(field, ".")+1:]] = err
return rsp
ps:removeTopStruct主要用作字符串的切分,应为用翻译成中文,返回的key里面前半段还是有英文,做切分处理
(4).在controller中使用HandleValidatorError函数
package controller
import (
"github.com/gin-gonic/gin"
"go_gin/utils"
"go_gin/forms"
"net/http"
func PasswordLogin(c *gin.Context) {
PasswordLoginForm := forms.PasswordLoginForm{
if err := c.ShouldBind(&PasswordLoginForm); err != nil {
utils.HandleValidatorError(c, err)
return
c.JSON(http.StatusOK, gin.H{
"msg": "sussess",
ps:ShouldBind()函数里面传入PasswordLoginForm实例,会根据结构体的tag校验字段
(5).在main.go中使用初始化字段校验器
在 main.go 中写入
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
(6).测试以上步骤
一个简单的字段校验器就完成了,我们现在验证下 \
<1>.参数错误时:
<1>.参数正确时:
中文的错误校验就显示出来了,如果你做到这一步,并且测试通过
就可以继续往下做~
3.自定义校验
结构体的tag都校验功能比较基础,但是有些复杂的校验需求,我们必须得自定义校验器才能做到
现在假设一个需求,前端传递的用户名必须得是手机号格式:
连续数字11位
数字1开头
了解了需求,我们大概知道校验函数用正则匹配好做一些
(1).定义校验函数
在 utils/valicator 中 编写
func ValidateMobile(fl validator.FieldLevel) bool {
mobile := fl.Field().String()
ok, _ := regexp.MatchString(`^1([38][0-9]|14[579]|5[^4]|16[6]|7[1-35-8]|9[189])\d{8}$`, mobile)
if !ok{
return false
return true
(2).定义注册自定义校验tag函数
在 initialize/validator中加入
type Func func(fl validator.FieldLevel) bool
func RegisterValidatorFunc(v *validator.Validate, tag string, msgStr string, fn Func) {
_ = v.RegisterValidation(tag, validator.Func(fn))
_ = v.RegisterTranslation(tag, global.Trans, func(ut ut.Translator) error {
return ut.Add(tag, "{0}"+msgStr, true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T(tag, fe.Field())
return t
return
(3).添加到InitTrans函数中去
我们写好了一个校验函数和一个注册自定义校验tag函数 ,
这时候我们把他们加入到InitTrans函数中就可以实现自定义校验了~
在 initialize/validator 的中InitTrans函数中加入
最后--测试环节
(1). PasswordLoginForm上加上mobile的tag
在 forms/user.go中 编写
(2).测试
name字段不符合自定义字段的校验逻辑,这时候就会报错
当你完成到这一步的时候说明你已经掌握了自定义校验器的使用
下一篇章介绍统一的response返回处理
如果这系列的文章对你有有用,请点赞和留言吧~