Golang:验证类型为字符串的结构字段是否为特定值之一

huangapple go评论79阅读模式
英文:

Golang: Validate Struct field of type string to be one of specific values

问题

golang版本:1.18.3

验证器:github.com/go-playground/validator/v10

我想在加载到嵌套结构数据结构后验证传入的JSON有效负载。这是我的传入JSON有效负载:

{
  "name": "Duro",
  "gender": "MALE",
  "tier": 3,
  "mobileNumber": "0356874586",
  "address": {
    "city": "Tokyo",
    "street": "Shibaura St"
  },
  "children":[
    {
      "title": "Mr",
      "lastName": "Takayashi"
    }
  ],
  "isEmployed": false,
  "requestedAt": "2022-01-10T03:30:12.639Z"
}

这是我的user.go文件:

package main

type User struct {
    Name                   string                     `validate:"required"`
    Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
    Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
    MobileNumber           string                     `validate:"required"`
    Email                  string
    Address                *Address  `validate:"required"`
    Children               []Child   `validate:"required,dive"`
    IsEmployed             *bool     `validate:"required"`
    PreferredContactMethod string    `validate:"oneof=EMAIL PHONE POST SMS"`
    RequestedAt            time.Time `validate:"required"`
}
type Address struct {
    City   string `validate:"required"`
    Street string `validate:"required"`
}

type Child struct {
    Title     string `validate:"required"`
    FirstName string
    LastName  string `validate:"required"`
}

这是我的测试函数:

func TestUserPayload(t *testing.T) {
    validate := validator.New()
    var u User

    err := json.Unmarshal([]byte(jsonData), &u)
    if err != nil {
        panic(err)
    }

    err := validate.Struct(&u)
    if err != nil {
        t.Errorf("error %v", err)
    }
}

这个测试失败并显示错误:

error Key: 'User.PreferredContactMethod' Error:Field validation for 'PreferredContactMethod' failed on the 'oneof' tag

这是因为Go将空字符串赋给User.PreferredContactMethod结构字段。由于PreferredContactMethod不是必需字段,当JSON有效负载中没有它时,我不想看到这个验证错误。我该如何避免在JSON有效负载中没有preferredContactMethod字段时出现此错误消息?

如果您有更好的方法来实现此验证,也很乐意听取。

这是Go Playground中的代码

英文:

golang version: 1.18.3

validator: github.com/go-playground/validator/v10

I want to validate an incoming JSON payload after loaded into nested struct data structure. Here's my incoming JSON payload,

{
  "name": "Duro",
  "gender": "MALE",
  "tier": 3,
  "mobileNumber": "0356874586",
  "address": {
    "city": "Tokyo",
    "street": "Shibaura St"
  },
  "children":[
    {
      "title": "Mr",
      "lastName": "Takayashi"
    }
  ],
  "isEmployed": false,
  "requestedAt": "2022-01-10T03:30:12.639Z"
}

Here's my user.go file,

package main

type User struct {
    	Name                   string                     `validate:"required"`
    	Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
    	Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
    	MobileNumber           string                     `validate:"required"`
    	Email                  string
    	Address                *Address  `validate:"required"`
    	Children               []Child   `validate:"required,dive"`
    	IsEmployed             *bool     `validate:"required"`
    	PreferredContactMethod string    `validate:"oneof=EMAIL PHONE POST SMS"`
    	RequestedAt            time.Time `validate:"required"`
    }
    type Address struct {
    	City   string `validate:"required"`
    	Street string `validate:"required"`
    }
    
    type Child struct {
    	Title     string `validate:"required"`
    	FirstName string
    	LastName  string `validate:"required"`
    }

Here's my test function

func TestUserPayload(t *testing.T) {
	validate := validator.New()
	var u User

	err := json.Unmarshal([]byte(jsonData), &u)
	if err != nil {
		panic(err)
	}

	err := validate.Struct(&u)
	if err != nil {
		t.Errorf("error %v", err)
	}

}

This test fails with error,

error Key: 'User.PreferredContactMethod' Error:Field validation for 'PreferredContactMethod' failed on the 'oneof' tag

This happens because Go assigns empty string to User.PreferredContactMethod struct field. Since PreferredContactMethod is NOT a required field, I DON'T want to see this validation error when json payload doesn't have it.
How can I avoid this error message when the json payload doesn't have preferredContactMethod field?

If you have better alternatives to achieve this validation, happy to hear them as well.

Here's the code in Go Playground

答案1

得分: 6

使用omitemptyoneof结合起来,使验证器库忽略空值或未设置的值。

type User struct {
    Name                   string                     `validate:"required"`
    Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
    Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
    MobileNumber           string                     `validate:"required"`
    Email                  string
    Address                *Address  `validate:"required"`
    Children               []Child   `validate:"required,dive"`
    IsEmployed             *bool     `validate:"required"`
    PreferredContactMethod string    `validate:"omitempty,oneof=EMAIL PHONE POST SMS"`
    RequestedAt            time.Time `validate:"required"`
}
英文:

Utilize omitempty along with oneof to make the validator library ignore empty or unset values.

type User struct {
        Name                   string                     `validate:"required"`
        Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
        Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
        MobileNumber           string                     `validate:"required"`
        Email                  string
        Address                *Address  `validate:"required"`
        Children               []Child   `validate:"required,dive"`
        IsEmployed             *bool     `validate:"required"`
        PreferredContactMethod string    `validate:"omitempty,oneof=EMAIL PHONE POST SMS"`
        RequestedAt            time.Time `validate:"required"`
    }

答案2

得分: 1

使用https://github.com/marrow16/valix(https://pkg.go.dev/github.com/marrow16/valix)可以通过以下方式实现:

package main

import (
	"fmt"
	"time"

	"github.com/marrow16/valix"
)

type User struct {
	Name                   string    `json:"name" v8n:"required, notNull"`
	Gender                 string    `json:"gender" v8n:"required, notNull, &strtoken{'MALE','FEMALE'}"`
	Tier                   uint8     `json:"tier" v8n:"required, notNull, &mini{0}, &maxi{3}"`
	DateOfBirth            string    `json:"dateOfBirth" v8n:"notNull, &strisod"`
	MobileNumber           string    `json:"mobileNumber" v8n:"required, notNull, &strpatt{'^([0-9]{8,16})$'}"`
	Email                  string    `json:"email" v8n:"notNull"`
	Address                Address   `json:"address" v8n:"required, notNull"`
	Children               []Child   `json:"children" v8n:"required, notNull"`
	IsEmployed             bool      `json:"isEmployed" v8n:"required"`
	PreferredContactMethod string    `json:"preferredContactMethod" v8n:"notNull, &strtoken{'EMAIL','PHONE','POST','SMS'}"`
	RequestedAt            time.Time `json:"requestedAt" v8n:"required, notNull, &strisodt"`
}
type Address struct {
	City   string `json:"city" v8n:"required, notNull"`
	Street string `json:"street" v8n:"required, notNull"`
}

type Child struct {
	Title     string `json:"title" v8n:"required, notNull"`
	FirstName string `json:"firstName" v8n:"notNull"`
	LastName  string `json:"lastName" v8n:"required, notNull"`
}

const jsonData = `{
  "name": "Duro",
  "gender": "MALE",
  "tier": 3,
  "dateOfBirth": "2000-01-13",
  "mobileNumber": "0356874586",
  "address": {
    "city": "Tokyo",
    "street": "Shibaura St"
  },
  "children":[
    {
      "title": "Mr",
      "lastName": "Takayashi"
    }
  ],
  "isEmployed": false,
  "requestedAt": "2022-01-10T03:30:12.639Z"
}`

var UserRequestValidator = valix.MustCompileValidatorFor(User{}, nil)

func main() {
	user := User{}

	ok, violations, _ := UserRequestValidator.ValidateStringInto(jsonData, &user)

	if ok {
		fmt.Println("Validation successful")
	} else {
		fmt.Println(fmt.Sprintf("Validation failed! (violations: %d)", len(violations)))
		for _, v := range violations {
			fmt.Printf("%s (property: '%s', path: '%s')\n", v.Message, v.Property, v.Path)
		}
	}
}

Go playground链接:https://go.dev/play/p/3zrkZx97m-e

声明:我是Valix的作者。

英文:

Using https://github.com/marrow16/valix (https://pkg.go.dev/github.com/marrow16/valix) it could be achieved with the following:

package main
import (
"fmt"
"time"
"github.com/marrow16/valix"
)
type User struct {
Name                   string    `json:"name" v8n:"required, notNull"`
Gender                 string    `json:"gender" v8n:"required, notNull, &strtoken{['MALE','FEMALE']}"`
Tier                   uint8     `json:"tier" v8n:"required, notNull, &mini{0}, &maxi{3}"`
DateOfBirth            string    `json:"dateOfBirth" v8n:"notNull, &strisod"`
MobileNumber           string    `json:"mobileNumber" v8n:"required, notNull, &strpatt{'^([0-9]{8,16})$'}"`
Email                  string    `json:"email" v8n:"notNull"`
Address                Address   `json:"address" v8n:"required, notNull"`
Children               []Child   `json:"children" v8n:"required, notNull"`
IsEmployed             bool      `json:"isEmployed" v8n:"required"`
PreferredContactMethod string    `json:"preferredContactMethod" v8n:"notNull, &strtoken{['EMAIL','PHONE','POST','SMS']}"`
RequestedAt            time.Time `json:"requestedAt" v8n:"required, notNull, &strisodt"`
}
type Address struct {
City   string `json:"city" v8n:"required, notNull"`
Street string `json:"street" v8n:"required, notNull"`
}
type Child struct {
Title     string `json:"title" v8n:"required, notNull"`
FirstName string `json:"firstName" v8n:"notNull"`
LastName  string `json:"lastName" v8n:"required, notNull"`
}
const jsonData = `{
"name": "Duro",
"gender": "MALE",
"tier": 3,
"dateOfBirth": "2000-01-13",
"mobileNumber": "0356874586",
"address": {
"city": "Tokyo",
"street": "Shibaura St"
},
"children":[
{
"title": "Mr",
"lastName": "Takayashi"
}
],
"isEmployed": false,
"requestedAt": "2022-01-10T03:30:12.639Z"
}`
var UserRequestValidator = valix.MustCompileValidatorFor(User{}, nil)
func main() {
user := User{}
ok, violations, _ := UserRequestValidator.ValidateStringInto(jsonData, &user)
if ok {
fmt.Println("Validation successful")
} else {
fmt.Println(fmt.Sprintf("Validation failed! (violations: %d)", len(violations)))
for _, v := range violations {
fmt.Printf("%s (property: '%s', path: '%s')\n", v.Message, v.Property, v.Path)
}
}
}

Go playground here: https://go.dev/play/p/3zrkZx97m-e

Disclosure: I am the author of Valix

huangapple
  • 本文由 发表于 2022年7月9日 01:04:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/72914728.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定