gin工程错误码翻译


一、背景

  • 需要对请求参数的校验,防止用户的恶意请求。
  • 需要对代码错误码进行翻译,返回中文
  • gin框架使用github.com/go-playground/validator进行参数校验,
  • 在定义结构体时使用 bindingvalidate, tag 标识相关校验规则

二、安装

# 命令行安装
$ go get github.com/go-playground/validator/v10

# 项目中引入
import "github.com/go-playground/validator/v10"

三、样例

package main

import (
    "fmt"
    "net/http"

    "github.com/gin-gonic/gin"
)

type RegisterRequest struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
    Nickname string `json:"nickname" binding:"required"`
    Email    string `json:"email" binding:"required,email"`
    Age      uint8  `json:"age" binding:"gte=1,lte=120"`
}

func main() {
    router := gin.Default()
    router.POST("register", Register)
    router.Run(":8080")
}

func Register(c *gin.Context) {
    var r RegisterRequest
    err := c.ShouldBindJSON(&r)
    if err != nil {
        c.JSON(http.StatusOK, gin.H{"msg": err.Error()})
        return
    }
    c.JSON(http.StatusOK, "success")
}

四、常见约束

字符串约束

  • excludesall:不包含参数中任意的 UNICODE 字符,例如excludesall=ab;
  • excludesrune:不包含参数表示的 rune 字符,excludesrune=asong;
  • startswith:以参数子串为前缀,例如startswith=hi;
  • endswith:以参数子串为后缀,例如endswith=bye。
  • contains=:包含参数子串,例如contains=email;
  • containsany:包含参数中任意的 UNICODE 字符,例如containsany=ab;
  • containsrune:包含参数表示的 rune 字符,例如`containsrune=asong;
  • excludes:不包含参数子串,例如excludes=email;

    范围约束

    范围约束的字段类型分为三种:
  • 对于数值,我们则可以约束其值
  • 对于切片、数组和map,我们则可以约束其长度
  • 对于字符串,我们则可以约束其长度

tag介绍:

  • ne:不等于参数值,例如ne=5;
  • gt:大于参数值,例如gt=5;
  • gte:大于等于参数值,例如gte=50;
  • lt:小于参数值,例如lt=50;
  • lte:小于等于参数值,例如lte=50;
  • oneof:只能是列举出的值其中一个,这些值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围,例如oneof=male female。
  • eq:等于参数值,注意与len不同。对于字符串,eq约束字符串本身的值,而len约束字符串长度。例如eq=10;
  • len:等于参数值,例如len=10;
  • max:小于等于参数值,例如max=10;
  • min:大于等于参数值,例如min=10

Fields约束

  • eqfield:定义字段间的相等约束,用于约束同一结构体中的字段。例如:eqfield=Password
  • eqcsfield:约束统一结构体中字段等于另一个字段(相对),确认密码时可以使用,例如:eqfiel=ConfirmPassword
  • nefield:用来约束两个字段是否相同,确认两种颜色是否一致时可以使用,例如:nefield=Color1
  • necsfield:约束两个字段是否相同(相对)

常用约束

  • unique:指定唯一性约束,不同类型处理不同:

    • 对于map,unique约束没有重复的值
    • 对于数组和切片,unique没有重复的值
    • 对于元素类型为结构体的碎片,unique约束结构体对象的某个字段不重复,使用unique=field指定字段名
  • email:使用email来限制字段必须是邮件形式,直接写eamil即可,无需加任何指定。

  • omitempty:字段未设置,则忽略

  • -:跳过该字段,不检验;

  • |:使用多个约束,只需要满足其中一个,例如rgb|rgba;

  • required:字段必须设置,不能为默认值;

实践使用

注册初始化

var trans ut.Translator

func init() {
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			var name string
			for _, t := range []string{"json", "form", "uri", "query"} {
				name = strings.SplitN(fld.Tag.Get(t), ",", 2)[0]
				if name != "" {
					if name == "-" {
						return ""
					}
					return name
				}
			}

			return name
		})

		trans, _ = ut.New(zh.New()).GetTranslator("zh")
		_ = zhTranslations.RegisterDefaultTranslations(v, trans)
	}
}

func Translate(err error) string {
	var errs validator.ValidationErrors
	if !errors.As(err, &errs) {
		return e.InvalidArgs.Error()
	} else {
		return errs[0].Translate(trans)
	}
}

翻译校验

type UsersRequest struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
    Nickname string `json:"nickname" binding:"required"`
    Email    string `json:"email" binding:"required,email"`
    Age      uint8  `json:"age" binding:"gte=1,lte=120"`
}


func (u *userController) Users(c *gin.Context) {
	var req types.UsersRequest
	if err := c.ShouldBind(&req); err != nil {
		zap.L().Error("request param error", zap.Error(err))
		response.WithMsg(c, e.InvalidArgs, validation.Translate(err))
		return
	}

  // 注册逻辑...

	response.Success(c, nil)
}

Author: stream
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source stream !
  TOC