| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- package validator
- import (
- "fmt"
- "regexp"
- "strings"
- "unicode/utf8"
- validate "git.ikuban.com/server/kubanapis/kuban/api/validate"
- )
- // validateString 验证字符串
- func (e *Engine) validateString(
- vctx *ValidationContext,
- fieldRules *validate.FieldRules,
- rules *validate.StringRules,
- value string,
- fieldPath string,
- errors *ValidationErrors,
- ) error {
- // const 验证
- if rules.Const != nil && value != *rules.Const {
- defaultMsg := fmt.Sprintf("字段 %s 必须等于 '%s'", fieldPath, *rules.Const)
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- e.getErrorMessage(fieldRules, vctx, "const", defaultMsg),
- ))
- if vctx.ShouldFailFast() {
- return errors
- }
- }
- // 长度验证(字符数)
- runeCount := utf8.RuneCountInString(value)
- if rules.Len != nil && uint64(runeCount) != *rules.Len {
- defaultMsg := fmt.Sprintf("字段 %s 长度必须为 %d 个字符", fieldPath, *rules.Len)
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- e.getErrorMessage(fieldRules, vctx, "len", defaultMsg),
- ))
- }
- if rules.MinLen != nil && uint64(runeCount) < *rules.MinLen {
- defaultMsg := fmt.Sprintf("字段 %s 长度至少为 %d 个字符", fieldPath, *rules.MinLen)
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- e.getErrorMessage(fieldRules, vctx, "min_len", defaultMsg),
- ))
- }
- if rules.MaxLen != nil && uint64(runeCount) > *rules.MaxLen {
- defaultMsg := fmt.Sprintf("字段 %s 长度最多为 %d 个字符", fieldPath, *rules.MaxLen)
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- e.getErrorMessage(fieldRules, vctx, "max_len", defaultMsg),
- ))
- }
- // 字节长度验证
- byteLen := len(value)
- if rules.LenBytes != nil && uint64(byteLen) != *rules.LenBytes {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- fmt.Sprintf("字段 %s 字节长度必须为 %d", fieldPath, *rules.LenBytes),
- ))
- }
- if rules.MinBytes != nil && uint64(byteLen) < *rules.MinBytes {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- fmt.Sprintf("字段 %s 字节长度至少为 %d", fieldPath, *rules.MinBytes),
- ))
- }
- if rules.MaxBytes != nil && uint64(byteLen) > *rules.MaxBytes {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidLength,
- fmt.Sprintf("字段 %s 字节长度最多为 %d", fieldPath, *rules.MaxBytes),
- ))
- }
- // 模式验证(正则表达式)
- if rules.Pattern != nil {
- matched, err := regexp.MatchString(*rules.Pattern, value)
- if err != nil {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidPattern,
- fmt.Sprintf("正则表达式错误: %v", err),
- ))
- } else if !matched {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidPattern,
- fmt.Sprintf("字段 %s 格式不正确", fieldPath),
- ))
- }
- }
- // 前缀/后缀验证
- if rules.Prefix != nil && !strings.HasPrefix(value, *rules.Prefix) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 必须以 '%s' 开头", fieldPath, *rules.Prefix),
- ))
- }
- if rules.Suffix != nil && !strings.HasSuffix(value, *rules.Suffix) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 必须以 '%s' 结尾", fieldPath, *rules.Suffix),
- ))
- }
- // 包含/不包含验证
- if rules.Contains != nil && !strings.Contains(value, *rules.Contains) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 必须包含 '%s'", fieldPath, *rules.Contains),
- ))
- }
- if rules.NotContains != nil && strings.Contains(value, *rules.NotContains) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 不能包含 '%s'", fieldPath, *rules.NotContains),
- ))
- }
- // in 验证
- if len(rules.In) > 0 {
- found := false
- for _, allowed := range rules.In {
- if value == allowed {
- found = true
- break
- }
- }
- if !found {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 的值不在允许的列表中", fieldPath),
- ))
- }
- }
- // not_in 验证
- if len(rules.NotIn) > 0 {
- for _, disallowed := range rules.NotIn {
- if value == disallowed {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidValue,
- fmt.Sprintf("字段 %s 的值在禁止列表中", fieldPath),
- ))
- break
- }
- }
- }
- // Well-known 格式验证
- switch rules.WellKnown.(type) {
- case *validate.StringRules_Email:
- if !isValidEmail(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的邮箱地址", fieldPath),
- ))
- }
- case *validate.StringRules_Uuid:
- if !isValidUUID(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的 UUID", fieldPath),
- ))
- }
- case *validate.StringRules_Uri:
- if !isValidURI(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的 URI", fieldPath),
- ))
- }
- case *validate.StringRules_ChineseMobile:
- if !isValidChineseMobile(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的中国手机号", fieldPath),
- ))
- }
- case *validate.StringRules_ChineseIdCard:
- if !isValidChineseIDCard(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的中国身份证号", fieldPath),
- ))
- }
- case *validate.StringRules_ChineseName:
- if !isValidChineseName(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的中文姓名", fieldPath),
- ))
- }
- case *validate.StringRules_ChinesePostcode:
- if !isValidChinesePostcode(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 不是有效的中国邮政编码", fieldPath),
- ))
- }
- }
- // 字符类型验证
- if rules.Numeric != nil && *rules.Numeric {
- if !regexp.MustCompile(`^\d+$`).MatchString(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 必须只包含数字", fieldPath),
- ))
- }
- }
- if rules.Alpha != nil && *rules.Alpha {
- if !regexp.MustCompile(`^[a-zA-Z]+$`).MatchString(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 必须只包含字母", fieldPath),
- ))
- }
- }
- if rules.Alphanumeric != nil && *rules.Alphanumeric {
- if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 必须只包含字母和数字", fieldPath),
- ))
- }
- }
- // 大小写验证
- if rules.Lowercase != nil && *rules.Lowercase {
- if value != strings.ToLower(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 必须是小写", fieldPath),
- ))
- }
- }
- if rules.Uppercase != nil && *rules.Uppercase {
- if value != strings.ToUpper(value) {
- errors.Add(NewValidationError(
- fieldPath,
- ErrCodeInvalidFormat,
- fmt.Sprintf("字段 %s 必须是大写", fieldPath),
- ))
- }
- }
- return nil
- }
- // Helper functions for format validation
- func isValidEmail(email string) bool {
- // 简化的邮箱验证正则
- pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
- matched, _ := regexp.MatchString(pattern, email)
- return matched
- }
- func isValidUUID(uuid string) bool {
- pattern := `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`
- matched, _ := regexp.MatchString(pattern, uuid)
- return matched
- }
- func isValidURI(uri string) bool {
- // 简化的 URI 验证
- return strings.Contains(uri, "://") || strings.HasPrefix(uri, "/")
- }
|