在上一节我们介绍到,gin可以使用ShouldBind方法把参数绑定到结构体,但是没有介绍到参数校验的方式,这节我们来介绍参数校验和校验失败后转换成中文返回前端。
1.数据校验
下面我们开始一个简单的例子:
- 在根目录的requests目录下新建一个test_request.go
package requests//测试请求结构体 该结构体定义了请求的参数和校验规则type TestRequest struct { Username string `form:"username" binding:"required"`}
- 在根目录的api目录下新建一个test.go的控制器,定义test控制器
package apiimport ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http")func Test(c *gin.Context) { //实例化一个TestRequest结构体,用于接收参数 testStruct := requests.TestRequest{} //接收请求参数 err := c.ShouldBind(&testStruct) //判断参数校验是否通过,如果不通过,把错误返回给前端 if err != nil { c.JSON(http.StatusOK, gin.H{"error": err.Error()}) return } //校验通过,返回请求参数 c.JSON(http.StatusOK, gin.H{"params": testStruct})}
- 在根目录的routers下定义/test路由,分别新建init.go和test.go文件初始化路由.
test.go
package routersimport ( "cn.sockstack/gin_demo/api" "github.com/gin-gonic/gin")func test(r *gin.Engine) { //定义/test路由 r.GET("/test", api.Test)}
init.go
package routersimport "github.com/gin-gonic/gin"func Init(r *gin.Engine) { //注册test路由 test(r)}
- 在main.go中注册路由
package main// 导入gin包import ( "cn.sockstack/gin_demo/pkg/config" "cn.sockstack/gin_demo/routers" "fmt" "github.com/gin-gonic/gin")// 入口函数func main() { // 初始化一个http服务对象 r := gin.Default() //注册路由 routers.Init(r) r.Run(fmt.Sprintf("%s:%d", config.Server.Address, config.Server.Port)) // 监听并在 0.0.0.0:8081 上启动服务}
- 运行并访问localhost:8081/test,不带参数会报错
{ "error": "Key: 'TestRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag"}
- 运行并访问localhost:8081/test?username=sockstack,则返回响应的参数。
{ "params": { "Username": "sockstack" }}
上面的例子已经可以实现参数校验和接收参数了,但是校验不通过的时候返回的提示是英文的,下面我们介绍一下怎么把错误转成中文返回。
2.校验参数失败提示自动翻译
通过查看代码我们发现gin默认的校验器使用的是validator包,并且查看文档发现validator是可以把英文错误翻译成中文的。
package mainimport ( "fmt" "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" en_translations "github.com/go-playground/validator/v10/translations/en")// User contains user informationtype User struct { FirstName string `validate:"required"` LastName string `validate:"required"` Age uint8 `validate:"gte=0,lte=130"` Email string `validate:"required,email"` FavouriteColor string `validate:"iscolor"` // alias for 'hexcolor|rgb|rgba|hsl|hsla' Addresses []*Address `validate:"required,dive,required"` // a person can have a home and cottage...}// Address houses a users address informationtype Address struct { Street string `validate:"required"` City string `validate:"required"` Planet string `validate:"required"` Phone string `validate:"required"`}// use a single instance , it caches struct infovar ( uni *ut.UniversalTranslator validate *validator.Validate)func main() { // NOTE: ommitting allot of error checking for brevity en := en.New() uni = ut.New(en, en) // this is usually know or extracted from http 'Accept-Language' header // also see uni.FindTranslator(...) trans, _ := uni.GetTranslator("en") validate = validator.New() en_translations.RegisterDefaultTranslations(validate, trans) translateAll(trans) translateIndividual(trans) translateOverride(trans) // yep you can specify your own in whatever locale you want!}func translateAll(trans ut.Translator) { type User struct { Username string `validate:"required"` Tagline string `validate:"required,lt=10"` Tagline2 string `validate:"required,gt=1"` } user := User{ Username: "Joeybloggs", Tagline: "This tagline is way too long.", Tagline2: "1", } err := validate.Struct(user) if err != nil { // translate all error at once errs := err.(validator.ValidationErrors) // returns a map with key = namespace & value = translated error // NOTICE: 2 errors are returned and you'll see something surprising // translations are i18n aware!!!! // eg. '10 characters' vs '1 character' fmt.Println(errs.Translate(trans)) }}func translateIndividual(trans ut.Translator) { type User struct { Username string `validate:"required"` } var user User err := validate.Struct(user) if err != nil { errs := err.(validator.ValidationErrors) for _, e := range errs { // can translate each error one at a time. fmt.Println(e.Translate(trans)) } }}func translateOverride(trans ut.Translator) { validate.RegisterTranslation("required", trans, func(ut ut.Translator) error { return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details }, func(ut ut.Translator, fe validator.FieldError) string { t, _ := ut.T("required", fe.Field()) return t }) type User struct { Username string `validate:"required"` } var user User err := validate.Struct(user) if err != nil { errs := err.(validator.ValidationErrors) for _, e := range errs { // can translate each error one at a time. fmt.Println(e.Translate(trans)) } }}
那么我们改造gin的校验提示。
- 在requests目录下新建init.go文件
package requestsimport ( "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" zh_translations "github.com/go-playground/validator/v10/translations/zh")var ( uni *ut.UniversalTranslator validate *validator.Validate trans ut.Translator)func init() { //注册翻译器 zh := zh.New() uni = ut.New(zh, zh) trans, _ = uni.GetTranslator("zh") //获取gin的校验器 validate := binding.Validator.Engine().(*validator.Validate) //注册翻译器 zh_translations.RegisterDefaultTranslations(validate, trans)}//Translate 翻译错误信息func Translate(err error) map[string][]string { var result = make(map[string][]string) errors := err.(validator.ValidationErrors) for _, err := range errors{ result[err.Field()] = append(result[err.Field()], err.Translate(trans)) } return result}
- 修改控制器
package apiimport ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http")func Test(c *gin.Context) { //实例化一个TestRequest结构体,用于接收参数 testStruct := requests.TestRequest{} //接收请求参数 err := c.ShouldBind(&testStruct) //判断参数校验是否通过,如果不通过,把错误返回给前端 if err != nil { c.JSON(http.StatusOK, gin.H{"error": requests.Translate(err)}) return } //校验通过,返回请求参数 c.JSON(http.StatusOK, gin.H{"params": testStruct})}
- 运行并访问localhost:8081/test,不带参数会报错,但是报错信息已经翻译成中文了
{ "error": { "Username": [ "Username为必填字段" ] }}
文章来源:智云一二三科技
文章标题:参数校验错误信息中文处理
文章地址:https://www.zhihuclub.com/5911.shtml