Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Golang web参数校验

Golang中web参数校验的实现

作者:Asakeiii

本文介绍了使用Gin框架进行参数校验的几种方法,包括JSON、URL查询、表单数据的校验,常用校验规则,自定义错误信息和自定义校验规则,具有一定的参考价值,感兴趣的可以了解一下

参数校验

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

基本用法

JSON

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

type Login struct {
	User     string `json:"user" binding:"required"`
	Password string `json:"password" 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"})
}

URL

除了处理 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"`
}
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"`
}
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参数校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文