只需一文:了解validator标签以轻松验证

本文正在参加金石计划附加挑战赛------第 二 期命题

1 简介

输入验证对于任何软件应用程序中的数据完整性和安全性都至关重要。 Go 是一种功能强大且流行的编程语言,以其简单性和性能而闻名,它提供"validator"包 (v10) 以实现高效验证。

合理使用验证器可以增强应用程序健壮性。 并且使用输入和输出验证也有助于提高服务安全和用户使用体验。

2 工具和库Validator

Validator.v10这可以帮助更简便快捷地实现参数校验和安全传递变量。

框架gin内置了 github.com/go-playground/validator/v10,用于字段验证。 可以自定义校验规则,满足复杂需求。我们可以灵活地创建针对您的特定应用程序要求定制的自定义验证器。 自定义验证器允许您对数据执行特定于域的规则,从而确保更精确的输入验证。

以下是创建名为 PercentageValidator 的自定义验证程序的示例

3 示例:标签校验

go 复制代码
	import (
		"github.com/go-playground/validator/v10"
		"github.com/gin-gonic/gin"
	)

	type User struct {
		Email string `json:"email" binding:"required,email"`
		Age   int    `json:"age" binding:"gte=0,lte=120"` // 年龄范围 0-120
	}

	var validate *validator.Validate

	func main() {
		validate = validator.New()

		r := gin.Default()
		r.POST("/validate", func(c *gin.Context) {
			var user User
			if err := c.ShouldBindJSON(&user); err != nil {
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}

			c.JSON(http.StatusOK, gin.H{"message": "Validation passed!"})
		})
		r.Run()
	}

将验证接收 JSON 格式数据的简单 API 终端节点的输入参数。 在此示例中,该函数检查输入值是否表示有效百分比(范围从 0 到 120)。 Validator验证器支持各种数据类型,包括 strings、integers 和 floats

4 更复杂的校验

以下为使用步骤

  • 1 使用自定义验证器

您需要在执行输入验证之前将其注册到 Validator V10。

您可以在合适的位置通过以下方式注册它:

go 复制代码
	validate := validator.New()

	// Registering the custom validator
	validate.RegisterValidation("rangeAge",  ageValidator)
  • 2 使用校验函数

检测输入的数字是否在100以内

css 复制代码
	func ageValidator(fl validator.FieldLevel) bool {
		maxPercent := decimal.NewFromInt(1)
		minPercent := decimal.NewFromInt(0)

		quantity := decimal.NewFromInt(100)

		switch v := fl.Field(); v.Kind() {
		case reflect.String:
			val, err := decimal.NewFromString(v.String())
			logs.Println("val before /100 :", val)
			val = val.Div(quantity) // / 100.0
			logs.Println("val after /100 :", val)

			if err == nil && val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
				return true
			}
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			val := decimal.NewFromInt(v.Int())
			logs.Println("val before /100 :", val)
			val = val.Div(quantity)
			logs.Println("val after /100 :", val)
			if val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
				return true
			}
		case reflect.Float32, reflect.Float64:
			val := decimal.NewFromFloat(v.Float())
			logs.Println("val before /100 :", val)
			val = val.Div(quantity)
			logs.Println("val after /100 :", val)
			if val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
				return true
			}
		default:
			return false
		}

		return false
	}
  • 3 绑定到定义数据结构:

接下来,我们定义一个 Go 结构体,它表示我们期望接收的输入的数据结构:

c 复制代码
type User struct {
    Username  string `json:"username" validate:"required,min=5,max=20"`
    Email     string `json:"email" validate:"required,email"`
    Age       int    `json:"age" validate:"ageValidator"`
}

在此示例中,我们有一个 User 结构,其中包含三个字段 Username、 Email、 和Age 。我们使用 Go Validator V10 语法添加了验证标签,以指定每个字段的验证规则。

现在,你可以将 validation 标签应用于 User 结构或任何其他数据结构中需要年龄验证的任何字段: age

通过创建自定义验证器,您可以根据特定应用程序的需求定制输入验证,从而确保数据处理的准确性和安全性。 它简化了以下任务:

添加特定于应用程序需求的自定义验证规则。
轻松转换错误消息以提供用户友好的反馈。
只需几行代码即可快速注册自定义验证器。
  • 4 输入验证

验证输入:validator提供了内置验证标签外 现在,让我们实现 API 端点并验证传入的数据:

go 复制代码
		// 注册新用户
		func createUserHandler(ctx *gin.Context) {
			// createUserHandler(w http.ResponseWriter, r *http.Request) {
			var user User
			//r := ctx.Request
			//w := ctx.Writer
			// Parse the JSON request and populate the User struct
			err := ctx.ShouldBindJSON(&user)
			logs.Printf("receive user:%#v \n", user)
			if err != nil {
				// http.Error(w, "Invalid JSON", http.StatusBadRequest)
				ctx.JSON(http.StatusBadRequest, "Invalid JSON")
				return
			}

			// Create a new validator instance and RegisterValidation
			validate := validator.New()
			validate.RegisterValidation("ageValidator", ageValidator)

			// Validate the User struct
			err = validate.Struct(user)
			if err != nil {
				// Validation failed, handle the error
				errors := err.(validator.ValidationErrors)
				// http.Error(w, fmt.Sprintf("Validation error: %s", errors), http.StatusBadRequest)
				ctx.JSON(http.StatusBadRequest, fmt.Sprintf("Validation error: %s", errors))

				return
			}

			// Validation passed, proceed to process the user data
			// Your application logic goes here...

			// Send a success response
			// w.WriteHeader(http.StatusOK)
			// fmt.Fprintf(w, "User created successfully!")
			ctx.JSON(http.StatusOK, gin.H{"data": fmt.Sprintf("Validation success: %v", user), "code": 10000})

		}

在此代码片段中,我们首先解析传入的 JSON 数据并填充 User 结构。 然后,我们创建一个新的验证程序实例,并使用 validate.Struct(user). 如果验证失败,将返回包含特定验证错误的错误消息。否则,我们的应用程序逻辑可以继续处理经过验证的数据。

3 校验和测试

绑定注册函数到路由

go 复制代码
	func WebValidationService(addr string) error {

		oe := gin.Default()
		busses := oe.Group("/user")
		busses.POST("/new", createUserHandler) //

		if addr == "" {
			addr = "0.0.0.0:2131"
		}
		fmt.Printf("board Web service:%v\n", addr)
		return oe.Run(addr)
	}

启动服务后调用校验测试

less 复制代码
		curl --location --request POST "http://127.0.0.1:881/user/new" ^
		--header "User-Agent: Apifox/1.0.0 (https://www.apifox.cn)" ^
		--header "Content-Type: application/json" ^
		--header "Accept: */*" ^
		--header "Host: 127.0.0.1:881" ^
		--header "Connection: keep-alive" ^
		--data-raw "{    \"email\":\"2312@da.com\",    \"age\":122}"

返回错误

css 复制代码
"Validation error: Key: 'User.Age' Error:Field validation for 'Age' failed on the 'ageValidator' tag"

3 完整的验证标签支持列表

  • Field

    scss 复制代码
      	Tag					Description
      	eqcsfield			Field Equals Another Field (relative)
      	eqfield				Field Equals Another Field
      	fieldcontains		Check the indicated characters are present in the Field
      	fieldexcludes		Check the indicated characters are not present in the field
      	gtcsfield			Field Greater Than Another Relative Field
      	gtecsfield			Field Greater Than or Equal To Another Relative Field
      	gtefield			Field Greater Than or Equal To Another Field
      	gtfield				Field Greater Than Another Field
      	ltcsfield			Less Than Another Relative Field
      	ltecsfield			Less Than or Equal To Another Relative Field
      	ltefield			Less Than or Equal To Another Field
      	ltfield				Less Than Another Field
      	necsfield			Field Does Not Equal Another Field (relative)
      	nefield				Field Does Not Equal Another Field
  • Network:

    css 复制代码
      	Tag					Description
      	cidr				Classless Inter-Domain Routing CIDR
      	cidrv4				Classless Inter-Domain Routing CIDRv4
      	cidrv6				Classless Inter-Domain Routing CIDRv6
      	datauri				Data URL
      	fqdn				Full Qualified Domain Name (FQDN)
      	hostname			Hostname RFC 952
      	hostname_port		HostPort
      	hostname_rfc1123	Hostname RFC 1123
      	ip					Internet Protocol Address IP
      	ip4_addr			Internet Protocol Address IPv4
      	ip6_addr			Internet Protocol Address IPv6
      	ip_addr				Internet Protocol Address IP
      	ipv4				Internet Protocol Address IPv4
      	ipv6				Internet Protocol Address IPv6
      	mac					Media Access Control Address MAC
      	tcp4_addr			Transmission Control Protocol Address TCPv4
      	tcp6_addr			Transmission Control Protocol Address TCPv6
      	tcp_addr			Transmission Control Protocol Address TCP
      	udp4_addr			User Datagram Protocol Address UDPv4
      	udp6_addr			User Datagram Protocol Address UDPv6
      	udp_addr			User Datagram Protocol Address UDP
      	unix_addr			Unix domain socket end point Address
      	uri					URI String
      	url					URL String
      	http_url			HTTP URL String
      	url_encoded			URL Encoded
      	urn_rfc2141			Urn RFC 2141 String
  • Strings:

    sql 复制代码
      	Tag					Description
      	alpha				Alpha Only
      	alphanum			Alphanumeric
      	alphanumunicode		Alphanumeric Unicode
      	alphaunicode		Alpha Unicode
      	ascii				ASCII
      	boolean				Boolean
      	contains			Contains
      	containsany			Contains Any
      	containsrune		Contains Rune
      	endsnotwith			Ends Not With
      	endswith			Ends With
      	excludes			Excludes
      	excludesall			Excludes All
      	excludesrune		Excludes Rune
      	lowercase			Lowercase
      	multibyte			Multi-Byte Characters
      	number				Number
      	numeric				Numeric
      	printascii			Printable ASCII
      	startsnotwith		Starts Not With
      	startswith			Starts With
      	uppercase			Uppercase
  • Format:

    typescript 复制代码
    Tag								Description
    base64							Base64 String
    base64url						Base64URL String
    base64rawurl					Base64RawURL String
    bic								Business Identifier Code (ISO 9362)
    bcp47_language_tag				Language tag (BCP 47)
    btc_addr						Bitcoin Address
    btc_addr_bech32					Bitcoin Bech32 Address (segwit)
    credit_card						Credit Card Number
    mongodb							MongoDB ObjectID
    mongodb_connection_string		MongoDB Connection String
    cron							Cron
    spicedb							SpiceDb ObjectID/Permission/Type
    datetime						Datetime
    e164							e164 formatted phone number
    email							E-mail String
    eth_addr						Ethereum Address
    hexadecimal						Hexadecimal String
    hexcolor						Hexcolor String
    hsl	HSL 						String
    hsla							HSLA String
    html							HTML Tags
    html_encoded					HTML Encoded
    isbn							International Standard Book Number
    isbn10							International Standard Book Number 10
    isbn13							International Standard Book Number 13
    issn							International Standard Serial Number
    iso3166_1_alpha2				Two-letter country code (ISO 3166-1 alpha-2)
    iso3166_1_alpha3				Three-letter country code (ISO 3166-1 alpha-3)
    iso3166_1_alpha_numeric			Numeric country code (ISO 3166-1 numeric)
    iso3166_2						Country subdivision code (ISO 3166-2)
    iso4217							Currency code (ISO 4217)
    json							JSON
    jwt								JSON Web Token (JWT)
    latitude						Latitude
    longitude						Longitude
    luhn_checksum					Luhn Algorithm Checksum (for strings and (u)int)
    postcode_iso3166_alpha2			Postcode
    postcode_iso3166_alpha2_field	Postcode
    rgb								RGB String
    rgba							RGBA String
    ssn								Social Security Number SSN
    timezone						Timezone
    uuid							Universally Unique Identifier UUID
    uuid3							Universally Unique Identifier UUID v3
    uuid3_rfc4122					Universally Unique Identifier UUID v3 RFC4122
    uuid4							Universally Unique Identifier UUID v4
    uuid4_rfc4122					Universally Unique Identifier UUID v4 RFC4122
    uuid5							Universally Unique Identifier UUID v5
    uuid5_rfc4122					Universally Unique Identifier UUID v5 RFC4122
    uuid_rfc4122					Universally Unique Identifier UUID RFC4122
    md4								MD4 hash
    md5								MD5 hash
    sha256							SHA256 hash
    sha384							SHA384 hash
    sha512							SHA512 hash
    ripemd128						RIPEMD-128 hash
    ripemd128						RIPEMD-160 hash
    tiger128						TIGER128 hash
    tiger160						TIGER160 hash
    tiger192						TIGER192 hash
    semver							Semantic Versioning 2.0.0
    ulid							Universally Unique Lexicographically Sortable Identifier ULID
    cve								Common Vulnerabilities and Exposures Identifier (CVE id)
  • Comparisons:

    sql 复制代码
    Tag					Description
    eq					Equals
    eq_ignore_case		Equals ignoring case
    gt					Greater than
    gte					Greater than or equal
    lt					Less Than
    lte					Less Than or Equal
    ne					Not Equal
    ne_ignore_case		Not Equal ignoring case
  • Other:

    sql 复制代码
    Tag							Description
    dir							Existing Directory
    dirpath						Directory Path
    file						Existing File
    filepath					File Path
    image						Image
    isdefault					Is Default
    len							Length
    max							Maximum
    min							Minimum
    oneof						One Of
    required					Required
    required_if					Required If
    required_unless				Required Unless
    required_with				Required With
    required_with_all			Required With All
    required_without			Required Without
    required_without_all		Required Without All
    excluded_if					Excluded If
    excluded_unless				Excluded Unless
    excluded_with				Excluded With
    excluded_with_all			Excluded With All
    excluded_without			Excluded Without
    excluded_without_all		Excluded Without All
    unique						Unique
  • Aliases:

    Tag Description iscolor hexcolor|rgb|rgba|hsl|hsla country_code iso3166_1_alpha2|iso3166_1_alpha3|iso3166_1_alpha_numeric

4 附:完整代码

go 复制代码
        type User struct {
                Email string `json:"email" binding:"required,email"`
                Age   int    `json:"age" validate:"ageValidator"` // 年龄范围 0-120
        }

        // 解析用户结构体
        func createUserHandler(ctx *gin.Context) {
                // createUserHandler(w http.ResponseWriter, r *http.Request) {
                var user User
                //r := ctx.Request
                //w := ctx.Writer
                // Parse the JSON request and populate the User struct
                err := ctx.ShouldBindJSON(&user)
                logs.Printf("receive user:%#v \n", user)
                if err != nil {
                        // http.Error(w, "Invalid JSON", http.StatusBadRequest)
                        ctx.JSON(http.StatusBadRequest, "Invalid JSON")
                        return
                }

                // Create a new validator instance
                validate := validator.New()
                validate.RegisterValidation("ageValidator", ageValidator)
                // Validate the User struct
                err = validate.Struct(user)
                if err != nil {
                        // Validation failed, handle the error
                        errors := err.(validator.ValidationErrors)
                        // http.Error(w, fmt.Sprintf("Validation error: %s", errors), http.StatusBadRequest)
                        ctx.JSON(http.StatusBadRequest, fmt.Sprintf("Validation error: %s", errors))

                        return
                }

                // Validation passed, proceed to process the user data
                // Your application logic goes here...

                // Send a success response
                // w.WriteHeader(http.StatusOK)
                // fmt.Fprintf(w, "User created successfully!")
                ctx.JSON(http.StatusOK, gin.H{"data": fmt.Sprintf("Validation success: %v", user), "code": 10000})

        }
        
        //校验函数
        func ageValidator(fl validator.FieldLevel) bool {
                maxPercent := decimal.NewFromInt(1)
                minPercent := decimal.NewFromInt(0)

                quantity := decimal.NewFromInt(100)

                switch v := fl.Field(); v.Kind() {
                case reflect.String:
                        val, err := decimal.NewFromString(v.String())
                        logs.Println("val before /100 :", val)
                        val = val.Div(quantity) // / 100.0
                        logs.Println("val after /100 :", val)

                        if err == nil && val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
                                return true
                        }
                case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                        val := decimal.NewFromInt(v.Int())
                        logs.Println("val before /100 :", val)
                        val = val.Div(quantity)
                        logs.Println("val after /100 :", val)
                        if val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
                                return true
                        }
                case reflect.Float32, reflect.Float64:
                        val := decimal.NewFromFloat(v.Float())
                        logs.Println("val before /100 :", val)
                        val = val.Div(quantity)
                        logs.Println("val after /100 :", val)
                        if val.Abs().GreaterThanOrEqual(minPercent) && val.Abs().LessThanOrEqual(maxPercent) {
                                return true
                        }
                default:
                        return false
                }

                return false
        }
        
        //服务路由
        func WebValidationService(addr string) error {

                oe := gin.Default()
                busses := oe.Group("/user")
                busses.POST("/new", createUserHandler) //

                if addr == "" {
                        addr = "0.0.0.0:2131"
                }
                fmt.Printf("board Web service:%v\n", addr)
                return oe.Run(addr)
        }

5 小结

Validator V10 提供的内置验证标签外,您还可以灵活地创建针对您的特定应用程序要求定制的自定义验证器。自定义验证器允许您对数据执行特定于域的规则,从而确保更精确的输入验证。

可以轻松地在 Go 应用程序中实现输入参数验证。验证输入参数有助于防止潜在的安全漏洞,并确保我们的应用程序使用的数据准确且一致。该库丰富的验证规则集使其成为使用 Go 的开发人员的强大工具。

请始终验证应用程序中的输入数据,以保证其可靠性和安全性.尽管输入验证很有用,但它不是一种最安全的措施,必要时使用安全插件仍然很重要。

相关推荐
m0_748255261 小时前
前端安全——敏感信息泄露
前端·安全
网络安全Jack1 小时前
什么是网络安全等级保护?
网络·安全·web安全
Dovir多多2 小时前
Python数据处理——re库与pydantic的使用总结与实战,处理采集到的思科ASA防火墙设备信息
网络·python·计算机网络·安全·网络安全·数据分析
Clockwiseee4 小时前
RCE常见姿势
安全·web安全·网络安全
denglei.4 小时前
在Nginx部署Web应用,如何保障后端API的安全
nginx·安全
2401_882727574 小时前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
追逐时光者5 小时前
.NET 在 Visual Studio 中的高效编程技巧集
后端·.net·visual studio
Smile灬凉城6665 小时前
robots协议
安全·php·robots
大梦百万秋6 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
斌斌_____6 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端