Go语言User模型自定义验证报错:未定义phone验证函数
用户注册自定义验证panic问题排查
我在实现用户注册的自定义验证逻辑,要求手机号匹配指定格式,密码同时包含字母、特殊字符和数字,但运行代码时触发panic,报错信息如下:
2023/03/24 19:42:13 http: panic serving 127.0.0.1:52498: Undefined validation function 'phone' on field 'Phone'
实现代码:
package models import ( "errors" "fmt" "regexp" "strings" "time" "github.com/go-playground/validator/v10" "go.mongodb.org/mongo-driver/bson/primitive" ) type User struct { UserId primitive.ObjectID `bson:"_id"` F_name *string `json:"f_name" validate:"required"` L_name *string `json:"l_name" validate:"required"` Username *string `json:"username"` Phone *string `json:"phone" validate:"required,phone"` Email *string `json:"email" validate:"required,email"` Pronoun *string `json:"pronoun" validate:"required,eq=he_him|eq=she_her|eq=them_they"` Password *string `json:"password" validate:"required,min=8,containsLetter,containsSpecialChar"` Role *string `json:"role" validate:"required,eq=customer"` Verified *bool `json:"verified"` Active *bool `json:"active"` Avatar *string `json:"avatar" validate:"required"` Qrcode *string `json:"qrcode"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } func ValidateUser(user *User) error { validate := validator.New() validate.RegisterValidation("containsLetter", func(fl validator.FieldLevel) bool { return strings.ContainsAny(fl.Field().String(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") }) validate.RegisterValidation("containsSpecialChar", func(fl validator.FieldLevel) bool { re := regexp.MustCompile(`[!@#\$%\^&\*\(\)_\+\-=\[\]\{\}\|\;\:\,\.\/\<\>\?\\]`) return re.MatchString(fl.Field().String()) }) validate.RegisterValidation("phone", func(fl validator.FieldLevel) bool { re := regexp.MustCompile(`^\+\d{1,3}\-\d{9,10}$`) return re.MatchString(fl.Field().String()) }) if user.Verified == nil { user.Verified = new(bool) *user.Verified = false } if user.Active == nil { user.Active = new(bool) *user.Active = true } if err := validate.Struct(user); err != nil { var errMessages []string for _, err := range err.(validator.ValidationErrors) { errMessages = append(errMessages, fmt.Sprintf("field %s failed validation: %s", err.Field(), err.Tag())) } return errors.New(strings.Join(errMessages, ", ")) } return nil }
问题分析
- 核心panic原因:
validator.RegisterValidation方法的返回值被忽略。该方法在注册自定义验证器失败时(如名称重复、参数非法)会返回error,但代码未处理这个错误,导致验证器注册失败后,validator找不到对应的phone验证函数,最终触发panic。 - 密码验证不完整:当前仅验证了密码包含字母和特殊字符,缺少数字检查,不符合需求。
修复方案
1. 处理验证器注册错误
每次调用RegisterValidation后必须检查返回的error,确保验证器注册成功,失败则直接返回错误。
2. 补全密码数字验证
新增containsNumber自定义验证器,并在Password字段的validate标签中添加该规则。
3. 优化正则表达式(可选)
简化特殊字符和手机号的正则,提升可读性。
修复后的代码
package models import ( "errors" "fmt" "regexp" "strings" "time" "github.com/go-playground/validator/v10" "go.mongodb.org/mongo-driver/bson/primitive" ) type User struct { UserId primitive.ObjectID `bson:"_id"` F_name *string `json:"f_name" validate:"required"` L_name *string `json:"l_name" validate:"required"` Username *string `json:"username"` Phone *string `json:"phone" validate:"required,phone"` Email *string `json:"email" validate:"required,email"` Pronoun *string `json:"pronoun" validate:"required,eq=he_him|eq=she_her|eq=them_they"` // 新增containsNumber验证规则 Password *string `json:"password" validate:"required,min=8,containsLetter,containsNumber,containsSpecialChar"` Role *string `json:"role" validate:"required,eq=customer"` Verified *bool `json:"verified"` Active *bool `json:"active"` Avatar *string `json:"avatar" validate:"required"` Qrcode *string `json:"qrcode"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } func ValidateUser(user *User) error { validate := validator.New() // 注册containsLetter并检查错误 if err := validate.RegisterValidation("containsLetter", func(fl validator.FieldLevel) bool { return strings.ContainsAny(fl.Field().String(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") }); err != nil { return fmt.Errorf("failed to register containsLetter validator: %w", err) } // 注册containsNumber验证器 if err := validate.RegisterValidation("containsNumber", func(fl validator.FieldLevel) bool { return strings.ContainsAny(fl.Field().String(), "0123456789") }); err != nil { return fmt.Errorf("failed to register containsNumber validator: %w", err) } // 注册containsSpecialChar并检查错误,简化正则 if err := validate.RegisterValidation("containsSpecialChar", func(fl validator.FieldLevel) bool { re := regexp.MustCompile(`[!@#$%^&*()_+\-=\[\]{}|;:'",.<>?\\/]`) return re.MatchString(fl.Field().String()) }); err != nil { return fmt.Errorf("failed to register containsSpecialChar validator: %w", err) } // 注册phone并检查错误 if err := validate.RegisterValidation("phone", func(fl validator.FieldLevel) bool { re := regexp.MustCompile(`^\+\d{1,3}-\d{9,10}$`) return re.MatchString(fl.Field().String()) }); err != nil { return fmt.Errorf("failed to register phone validator: %w", err) } if user.Verified == nil { user.Verified = new(bool) *user.Verified = false } if user.Active == nil { user.Active = new(bool) *user.Active = true } if err := validate.Struct(user); err != nil { var errMessages []string for _, err := range err.(validator.ValidationErrors) { errMessages = append(errMessages, fmt.Sprintf("field %s failed validation: %s", err.Field(), err.Tag())) } return errors.New(strings.Join(errMessages, ", ")) } return nil }
内容的提问来源于stack exchange,提问作者John Mark
相关产品推荐
相关产品推荐

