Golang中web参数校验的实现

作者:

文章目录
  • 确保我们的应用接收到的数据是格式正确并且安全的
  • 我们通过一个用户注册的例子来看看: type RegisterUser struct { // 用户名:必填,长度在 4 到 20 个字符之间 Username string `json:”username” binding:”required,min=4,max=20″` // 邮箱:必填,且必须是合法的邮箱格式 Email string `json:”email” binding:”required,email”` // 年龄:选填,但如果提供了,必须大于等于 18 岁,小于等于 100 岁 Age int `json:”age” binding:”gte=18,lte=100″` // 用户类型:必填,并且值必须是 ‘user’ 或 ‘admin’ 中的一个 UserType string `json:”user_type” binding:”required,oneof=user admin”` } min=4,max=20:用于字符串、数组等,限制其最小和最大长度。多个规则用逗号 , 分隔。 email:检查字符串是否符合标准的 email 格式。 gte=18:意思是 “Greater Than or Equal To”,即大于或等于 18。 lte=100:意思是 “Less Than or Equal To”,即小于或等于 100。 oneof=user admin:表示这个字段的值必须是后面列出的值之一(用空格分隔)。 func registerUserHandler(c *gin.Context) { var register RegisterUser err := c.ShouldBindJSON(&register) if err != nil { c.JSON(400, gin.H{“error”: err.Error()}) return } c.JSON(200, gin.H{ “username”: register.Username, “email”: register.Email, }) }
  • 核心思路是:捕获到校验错误后,我们不再直接把它整个返回,而是逐一检查是哪个字段的哪条规则出错了,然后根据这些信息,手动“翻译”成我们想展示的话。 func registerUserHandler(c *gin.Context) { var register RegisterUser err := c.ShouldBindJSON(&register) if err != nil { var verrs validator.ValidationErrors ok := errors.As(err, &verrs) if !ok { // 如果不是 ValidationErrors 类型的错误,直接返回原始错误 c.JSON(400, gin.H{“error”: err.Error()}) return } // 创建一个 map 来存放我们“翻译”后的错误信息 // key 是字段名,value 是错误信息 // 比如:{“Username”: “长度不能小于 4 个字符”} translatedErrors := make(map[string]string) for _, fe := range verrs { // fe.Field() 获取出错的字段名,比如 “Username” translatedErrors[fe.Field()] = getErrorMsg(fe) } c.JSON(400, gin.H{“errors”: translatedErrors}) return } c.JSON(200, gin.H{ “username”: register.Username, “email”: register.Email, }) } // 这个函数专门用来“翻译”错误信息 func getErrorMsg(fe validator.FieldError) string { // fe.Tag() 能获取到是哪个校验规则出错了,比如 “required”, “min”, “email” switch fe.Tag() { case “required”: return “这是必填项” case “min”: // fe.Param() 能获取到规则后面的参数,比如 “min=4” 中的 “4” return “长度不能小于 ” + fe.Param() + ” 个字符” case “max”: return “长度不能超过 ” + fe.Param() + ” 个字符” case “email”: return “请输入正确的邮箱地址” case “oneof”: return “必须是 ” + fe.Param() + ” 中的一个” default: return “未知错误” } }
  • 当内置的规则(像 required, email, min, max 等)不够用时,我们就需要自定义规则。比如,我们想强制要求“密码必须同时包含字母和数字”。 整个过程分为三步,我们一步一步来: 编写一个自定义校验函数这个函数需要一个特定的格式。它接收一个 validator.FieldLevel 类型的参数,并返回一个 bool 值。返回 true 表示校验通过,false 表示失败。 // isPasswordStrong 就是我们的自定义校验函数 func isPasswordStrong(fl validator.FieldLevel) bool { // fl.Field().String() 可以获取到字段的字符串值 password := fl.Field().String() hasLetter := false hasDigit := false // 遍历密码字符串,检查是否同时包含字母和数字 for _, char := range password { if unicode.IsLetter(char) { hasLetter = true } if unicode.IsDigit(char) { hasDigit = true } } return hasLetter && hasDigit } 将自定义函数“注册”到 Gin 的校验器里光写好函数还不行,我们得告诉 Gin 的校验器:“嘿,我这里有一个新的校验规则,它的名字叫 strong_password,对应的处理函数是 isPasswordStrong。” 这个注册过程通常在你的程序启动时(比如 main 函数里)完成。 // 从 Gin 的 binding 中获取底层的 validator 引擎 if v, ok := binding.Validator.Engine().(*validator.Validate); ok { // 注册我们的自定义校验函数 // 第一个参数是这个规则在 struct tag 中使用的名字 // 第二个参数是我们的函数 v.RegisterValidation(“strong_password”, isPasswordStrong) } 在 Struct Tag 中使用新规则一旦注册成功,我们就可以像使用任何内置规则一样,在 struct tag 中使用 strong_password 了。 到此这篇关于Golang中web参数校验的实现的文章就介绍到这了,更多相关Golang web参数校验内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: golang validator库参数校验实用技巧干货 golang validator参数校验的实现
  • 目录
    • 参数校验
    • 基本用法
      • JSON
      • URL
    • 常用的校验规则
      • 自定义错误信息
        • 自定义校验规则

          确保我们的应用接收到的数据是格式正确并且安全的

          假设我们正在开发一个登录接口,希望用户必须提供用户名和密码。在 Gin 中,我们首先会定义一个 Go 的 struct 来描述这个数据结构。

          type Login struct {
          	User     string `json:"user" binding:"required"`
          	Password string `json:"password" binding:"required"`
          }
          
          • json:“…” 标签: 这个标签告诉 Gin,当它解析传入的 JSON 数据时,JSON 中的 user 字段应该映射到我们结构体中的 User 字段。
          • binding:“required” 标签: 这是最核心的部分!binding 就是用来做数据校验的。required 是一个校验规则,意思是“这个字段是必填的,不能为空”。
          func loginHandler(c *gin.Context) {
          	var login Login
          
          	// 尝试将请求的 JSON body 绑定到 login 结构体上
          	// 在绑定的同时,Gin 会根据 "binding" 标签进行校验
          	err := c.ShouldBindJSON(&login)
          	if err != nil {
          		// 如果 err 不是 nil,说明校验失败了!
          		// 比如,某个 "required" 的字段没有被提供。
          		// Gin 通常会自动返回一个 400 Bad Request 错误。
          		c.JSON(400, gin.H{"error": err.Error()})
          		return
          	}
          
          	// 如果 err 是 nil,说明所有校验都通过了!
          	c.JSON(200, gin.H{"status": "you are logged in"})
          }
          
          

          除了处理 JSON,我们经常还需要处理来自 URL 查询(Query)和 HTML 表单(Form)的数据。
          Gin 的设计非常统一。我们的 struct 定义方式几乎不变,只需要更换一个绑定方法就可以了。

          如何校验 URL 查询参数,比如这样一个请求:GET /search?keyword=gin&page=1
          我们希望 keyword 是必填的,并且 page 必须是一个大于 0 的数字。
          首先,还是定义我们的 struct:

          type SearchQuery struct {
          	Keyword string `form:"keyword" binding:"required"`
          	Page    int    `form:"page"    binding:"required,gt=0"`
          }
          
          • 我们用了 form:“…” 标签,而不是 json:“…”。这个 form 标签既可以用于 URL 查询参数,也可以用于表单数据。
          • 我在 Page 字段的 binding 里加了一个新规则 gt=0,它的意思是 “greater than 0(必须大于0)。
          func searchHandler(c *gin.Context) {
              var query SearchQuery
          
              // 从 URL query 中绑定并校验参数
              // 注意这里换成了 ShouldBindQuery
              err := c.ShouldBindQuery(&query)
          
              if err != nil {
                  c.JSON(400, gin.H{"error": err.Error()})
                  return
              }
          
              // 校验通过!
              c.JSON(200, gin.H{
                  "message": "search parameters are valid",
                  "keyword": query.Keyword,
                  "page":    query.Page,
              })
          }
          

          我们通过一个用户注册的例子来看看:

          type RegisterUser struct {
          	// 用户名:必填,长度在 4 到 20 个字符之间
          	Username string `json:"username" binding:"required,min=4,max=20"`
          
          	// 邮箱:必填,且必须是合法的邮箱格式
          	Email string `json:"email" binding:"required,email"`
          
          	// 年龄:选填,但如果提供了,必须大于等于 18 岁,小于等于 100 岁
          	Age int `json:"age" binding:"gte=18,lte=100"`
          
          	// 用户类型:必填,并且值必须是 'user' 或 'admin' 中的一个
          	UserType string `json:"user_type" binding:"required,oneof=user admin"`
          }
          
          • min=4,max=20:用于字符串、数组等,限制其最小和最大长度。多个规则用逗号 , 分隔。
          • email:检查字符串是否符合标准的 email 格式。
          • gte=18:意思是 “Greater Than or Equal To”,即大于或等于 18。
          • lte=100:意思是 “Less Than or Equal To”,即小于或等于 100。
          • oneof=user admin:表示这个字段的值必须是后面列出的值之一(用空格分隔)。
          func registerUserHandler(c *gin.Context) {
          	var register RegisterUser
          	err := c.ShouldBindJSON(&register)
          	if err != nil {
          		c.JSON(400, gin.H{"error": err.Error()})
          		return
          	}
          
          	c.JSON(200, gin.H{
          		"username": register.Username,
          		"email":    register.Email,
          	})
          }
          

          核心思路是:捕获到校验错误后,我们不再直接把它整个返回,而是逐一检查是哪个字段的哪条规则出错了,然后根据这些信息,手动“翻译”成我们想展示的话。

          func registerUserHandler(c *gin.Context) {
          	var register RegisterUser
          	err := c.ShouldBindJSON(&register)
          	if err != nil {
          		var verrs validator.ValidationErrors
          		ok := errors.As(err, &verrs)
          		if !ok {
          			// 如果不是 ValidationErrors 类型的错误,直接返回原始错误
          			c.JSON(400, gin.H{"error": err.Error()})
          			return
          		}
          		// 创建一个 map 来存放我们“翻译”后的错误信息
          		// key 是字段名,value 是错误信息
          		// 比如:{"Username": "长度不能小于 4 个字符"}
          		translatedErrors := make(map[string]string)
          		for _, fe := range verrs {
          			// fe.Field() 获取出错的字段名,比如 "Username"
          			translatedErrors[fe.Field()] = getErrorMsg(fe)
          		}
          
          		c.JSON(400, gin.H{"errors": translatedErrors})
          		return
          	}
          
          	c.JSON(200, gin.H{
          		"username": register.Username,
          		"email":    register.Email,
          	})
          }
          
          // 这个函数专门用来“翻译”错误信息
          func getErrorMsg(fe validator.FieldError) string {
          	// fe.Tag() 能获取到是哪个校验规则出错了,比如 "required", "min", "email"
          	switch fe.Tag() {
          	case "required":
          		return "这是必填项"
          	case "min":
          		// fe.Param() 能获取到规则后面的参数,比如 "min=4" 中的 "4"
          		return "长度不能小于 " + fe.Param() + " 个字符"
          	case "max":
          		return "长度不能超过 " + fe.Param() + " 个字符"
          	case "email":
          		return "请输入正确的邮箱地址"
          	case "oneof":
          		return "必须是 " + fe.Param() + " 中的一个"
          	default:
          		return "未知错误"
          	}
          }
          

          当内置的规则(像 required, email, min, max 等)不够用时,我们就需要自定义规则。比如,我们想强制要求“密码必须同时包含字母和数字”。

          整个过程分为三步,我们一步一步来:

          1. 编写一个自定义校验函数
            这个函数需要一个特定的格式。它接收一个 validator.FieldLevel 类型的参数,并返回一个 bool 值。返回 true 表示校验通过,false 表示失败。
          // isPasswordStrong 就是我们的自定义校验函数
          func isPasswordStrong(fl validator.FieldLevel) bool {
              // fl.Field().String() 可以获取到字段的字符串值
              password := fl.Field().String()
              
              hasLetter := false
              hasDigit := false
          
              // 遍历密码字符串,检查是否同时包含字母和数字
              for _, char := range password {
                  if unicode.IsLetter(char) {
                      hasLetter = true
                  }
                  if unicode.IsDigit(char) {
                      hasDigit = true
                  }
              }
              
              return hasLetter && hasDigit
          }
          
          1. 将自定义函数“注册”到 Gin 的校验器里
            光写好函数还不行,我们得告诉 Gin 的校验器:“嘿,我这里有一个新的校验规则,它的名字叫 strong_password,对应的处理函数是 isPasswordStrong。”

            这个注册过程通常在你的程序启动时(比如 main 函数里)完成。

          // 从 Gin 的 binding 中获取底层的 validator 引擎
          	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
          		// 注册我们的自定义校验函数
          		// 第一个参数是这个规则在 struct tag 中使用的名字
          		// 第二个参数是我们的函数
          		v.RegisterValidation("strong_password", isPasswordStrong)
          	}
          
          1. 在 Struct Tag 中使用新规则
            一旦注册成功,我们就可以像使用任何内置规则一样,在 struct tag 中使用 strong_password 了。

          到此这篇关于Golang中web参数校验的实现的文章就介绍到这了,更多相关Golang web参数校验内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

          您可能感兴趣的文章:

          • golang validator库参数校验实用技巧干货
          • golang validator参数校验的实现

          站内搜索