ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Gin框架系列之表单验证

2022-02-15 08:32:45  阅读:275  来源: 互联网

标签:github http string err 验证 表单 gin Gin com


一、表单基本校验

Gin是一个Web框架,提供Web服务,所以很多功能是通过第三方插件集成进去的,这里使用了https://github.com/go-playground/validator来处理的。它实现了结构体值验证以及基于标签的单个字段。所以可以将请求体绑定到结构体模型上。

需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:"fieldname" 。

此外,Gin还提供了两套绑定方法:

1、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-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422,如果你希望更好地控制行为,请使用ShouldBind相关的方法

2、Should bind

  • Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML
  • Behavior - 这些方法底层使用 ShouldBindWith,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。

当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用MustBindWith或者BindingWith。

你还可以给字段指定特定规则的修饰符,如果一个字段用binding:"required"修饰,并且在绑定时该字段的值为空,那么将返回一个错误。

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type LoginForm struct {
    UserName string `form:"username" json:"username" binding:"required"`
    Password string `form:"password" json:"password" binding:"required"`
}

func main() {
    router := gin.Default()
    router.POST("/login", func(c *gin.Context) {

        var loginForm LoginForm
        if err := c.ShouldBind(&loginForm); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{
                "error": err.Error(),
            })
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "msg": "登录成功",
        })
    })
    router.Run(":8000")
}

然后可以通过客户端发送请求:

import requests

res = requests.post(url="http://127.0.0.1:8000/login", data={
    "username": "bily",
    "password": "123456"
})
print(res.text)

但是假如输入的请求体不符合后台验证规则,会出现下面的错误:

{"error":"Key: 'LoginForm.Password' Error:Field validation for 'Password' failed on the 'required' tag"}

那么怎么把上述的英文转成中文呢?

二、表单验证错误处理

 可以看到上面的错误提示都是一些后端定义的字段名以及英文提示,对于用户来说不是很友好,所以需要将其转成中文提示。

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "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"
    "net/http"
    "reflect"
    "strings"
)

var trans ut.Translator

// 初始化一个翻译器函数
func InitTrans(locale string) (err error) {
    // 修改gin框架中的validator引擎属性,实现定制
    v, ok := binding.Validator.Engine().(*validator.Validate)
    if ok {
        // 注册一个获取json的tag自定义方法
        v.RegisterTagNameFunc(func(field reflect.StructField) string {
            name := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]
            if name == "-" {
                return ""
            }
            return name
        })
    }
    zhT := zh.New()              //中文翻译器
    enT := en.New()              //英文翻译器
    uni := ut.New(enT, zhT, enT) // 配置默认翻译器、以及可支持的翻译器
    trans, ok = uni.GetTranslator(locale)
    if !ok {
        return fmt.Errorf("uni.GetTranslator(%s) error", locale)
    }
    switch locale {
    case "en":
        _ = en_translations.RegisterDefaultTranslations(v, trans)
    case "zh":
        _ = zh_translations.RegisterDefaultTranslations(v, trans)
    default:
        _ = en_translations.RegisterDefaultTranslations(v, trans)
    }
    return

}

type LoginForm struct {
    UserName string `form:"username" json:"username" binding:"required"`
    Password string `form:"password" json:"password" binding:"required"`
}

func main() {
    router := gin.Default()
    router.POST("/login", func(c *gin.Context) {
        // 调用翻译器函数
        if err := InitTrans("zh"); err != nil {
            fmt.Println("翻译器错误!")
            return
        }
        var loginForm LoginForm
        if err := c.ShouldBind(&loginForm); err != nil {
            if errors, ok := err.(validator.ValidationErrors); !ok {
                // 如果错误不能转化,不能进行翻译,就返回错误信息
                c.JSON(http.StatusOK, gin.H{
                    "error": errors.Error(),
                })
            } else {
                // 如果错误能进行翻译,就返回翻译后的错误信息
                c.JSON(http.StatusBadRequest, gin.H{
                    "error": errors.Translate(trans),
                })
            }
            return
        }

        c.JSON(http.StatusOK, gin.H{
            "msg": "登录成功",
        })
    })
    router.Run(":8000")
}

假如进行一个错误请求:

import requests

res = requests.post(url="http://127.0.0.1:8000/login", data={
    "password": "123456"
})
print(res.text)

如下为错误信息:

{"error":{"LoginForm.username":"username为必填字段"}}

着只解决了部分问题,但是字段没有翻译过来,所以还需要进行优化:

...
func removeTopStruct(fields map[string]string) map[string]string {
    rsp := map[string]string{}
    for key, value := range fields {
        rsp[key[strings.Index(key, ".")+1:]] = value
    }
    return rsp
}
...

在之前的错误处理main函数中引入该函数:

...
 else {
                // 如果错误能进行翻译,就返回翻译后的错误信息
                c.JSON(http.StatusBadRequest, gin.H{
                    "error": removeTopStruct(errors.Translate(trans)),
                })
            }
...

错误处理的效果为:

{"error":{"username":"username为必填字段"}}

 

标签:github,http,string,err,验证,表单,gin,Gin,com
来源: https://www.cnblogs.com/shenjianping/p/15885506.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有