README_VALIDATE.md 17 KB

Kuban API 验证框架

基于 buf/validate 优化设计的自定义字段验证框架,提供简化的 API、中文支持和业务规则集成。

目录

特性

🚀 相比 buf/validate 的优化

  1. 简化的 API 设计

    • 更直观的规则定义语法
    • 减少冗余配置
    • 更好的类型安全
  2. 增强的中文支持

    • 内置中文错误消息
    • 中文特定验证规则(手机号、身份证等)
    • 双语错误消息支持
  3. 业务规则集成

    • 开箱即用的中国业务验证规则
    • 手机号、身份证、统一社会信用代码等
    • 易于扩展的业务规则库
  4. 分组验证

    • 场景化验证支持
    • 创建、更新、审核等不同场景使用不同规则
    • 灵活的规则组合
  5. 保留 CEL 支持

    • 完整的 CEL 表达式支持
    • 灵活的自定义验证逻辑
    • 跨字段验证能力

快速开始

1. 引入依赖

import "kuban/api/validate.proto";
import "kuban/api/business_rules.proto";  // 可选:业务规则
import "kuban/api/rule_groups.proto";     // 可选:分组验证

2. 基本字段验证

message User {
  // 必填字符串,长度 2-50
  string name = 1 [(kuban.api.validate.field) = {
    required: true,
    string: {
      min_len: 2,
      max_len: 50
    }
  }];

  // 必填邮箱
  string email = 2 [(kuban.api.validate.field) = {
    required: true,
    string: {
      email: true
    }
  }];

  // 年龄范围 0-150
  int32 age = 3 [(kuban.api.validate.field) = {
    int32: {
      gte: 0,
      lte: 150
    }
  }];
}

3. 中文业务验证

message ChineseUser {
  // 中国手机号
  string mobile = 1 [(kuban.api.validate.field) = {
    required: true,
    string: {
      chinese_mobile: true
    }
  }];

  // 中国身份证
  string id_card = 2 [(kuban.api.validate.field) = {
    required: true,
    string: {
      chinese_id_card: true
    }
  }];

  // 中文姓名
  string name = 3 [(kuban.api.validate.field) = {
    required: true,
    string: {
      chinese_name: true
    }
  }];
}

核心概念

验证级别

框架支持三个级别的验证:

  1. 字段级别验证 - 验证单个字段的值
  2. 消息级别验证 - 验证整个消息的一致性(跨字段验证)
  3. Oneof 级别验证 - 验证 oneof 字段的选择

验证规则类型

  • 类型特定规则: 针对不同数据类型的验证规则(如 StringRules, Int32Rules)
  • 通用规则: 适用于所有类型的规则(如 required, ignore)
  • CEL 自定义规则: 使用 CEL 表达式的自定义验证逻辑
  • 业务规则: 预定义的业务场景验证规则

使用指南

字符串验证

message StringExample {
  // 长度限制
  string username = 1 [(kuban.api.validate.field).string = {
    min_len: 3,
    max_len: 20
  }];

  // 正则表达式
  string code = 2 [(kuban.api.validate.field).string = {
    pattern: "^[A-Z]{2}\\d{4}$"
  }];

  // 前缀/后缀
  string ref_id = 3 [(kuban.api.validate.field).string = {
    prefix: "REF_"
  }];

  // 枚举值
  string status = 4 [(kuban.api.validate.field).string = {
    in: ["pending", "approved", "rejected"]
  }];

  // 预定义格式
  string email = 5 [(kuban.api.validate.field).string.email = true];
  string url = 6 [(kuban.api.validate.field).string.uri = true];
  string uuid = 7 [(kuban.api.validate.field).string.uuid = true];
  string ip = 8 [(kuban.api.validate.field).string.ip = true];
}

数值验证

message NumberExample {
  // 范围验证
  int32 score = 1 [(kuban.api.validate.field).int32 = {
    gte: 0,
    lte: 100
  }];

  // 常量值
  int32 version = 2 [(kuban.api.validate.field).int32.const = 1];

  // 枚举值
  int32 priority = 3 [(kuban.api.validate.field).int32 = {
    in: [1, 2, 3, 4, 5]
  }];

  // 浮点数
  double price = 4 [(kuban.api.validate.field).double = {
    gt: 0.0,
    finite: true  // 不允许 NaN 和 Infinity
  }];
}

复杂类型验证

message ComplexExample {
  // 重复字段
  repeated string tags = 1 [(kuban.api.validate.field).repeated = {
    min_items: 1,
    max_items: 10,
    unique: true,
    items: {
      string: {
        min_len: 2,
        max_len: 20
      }
    }
  }];

  // Map 字段
  map<string, int32> scores = 2 [(kuban.api.validate.field).map = {
    min_pairs: 1,
    max_pairs: 100,
    keys: {
      string: { min_len: 1 }
    },
    values: {
      int32: { gte: 0, lte: 100 }
    }
  }];
}

CEL 自定义验证

message CELExample {
  string password = 1 [(kuban.api.validate.field) = {
    required: true,
    cel: [
      {
        id: "password.length",
        message: "密码长度必须在 8-32 之间",
        expression: "this.size() >= 8 && this.size() <= 32"
      },
      {
        id: "password.complexity",
        message: "密码必须包含大小写字母和数字",
        expression: "this.matches('(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d)')"
      }
    ]
  }];

  // 跨字段验证
  int64 start_time = 2;
  int64 end_time = 3 [(kuban.api.validate.field).cel = [{
    id: "time.range",
    message: "结束时间必须大于开始时间",
    expression: "this > this.start_time"
  }]];
}

消息级别验证

message MessageExample {
  option (kuban.api.validate.message).cel = [
    {
      id: "at_least_one_contact",
      message: "邮箱和手机号至少需要填写一个",
      expression: "has(this.email) || has(this.mobile)"
    }
  ];

  optional string email = 1;
  optional string mobile = 2;
}

业务规则

中国手机号验证

message MobileExample {
  // 简单用法
  string mobile = 1 [(kuban.api.validate.field).string.chinese_mobile = true];

  // 详细配置
  string mobile_advanced = 2 [(kuban.api.validate.field).string = {
    (kuban.api.validate.chinese_mobile_rules): {
      enabled: true,
      allowed_operators: [
        MOBILE_OPERATOR_CHINA_MOBILE,
        MOBILE_OPERATOR_CHINA_UNICOM,
        MOBILE_OPERATOR_CHINA_TELECOM
      ],
      allow_virtual: false,
      error_message: "请输入有效的中国大陆手机号"
    }
  }];
}

支持的运营商:

  • 中国移动 (MOBILE_OPERATOR_CHINA_MOBILE)
  • 中国联通 (MOBILE_OPERATOR_CHINA_UNICOM)
  • 中国电信 (MOBILE_OPERATOR_CHINA_TELECOM)
  • 中国广电 (MOBILE_OPERATOR_CHINA_BROADNET)

中国身份证验证

message IdCardExample {
  string id_card = 1 [(kuban.api.validate.field).string = {
    (kuban.api.validate.chinese_id_card_rules): {
      enabled: true,
      verify_checksum: true,      // 验证校验位
      verify_birth_date: true,    // 验证出生日期
      verify_area_code: true,     // 验证地区码
      allowed_types: [ID_CARD_TYPE_18_DIGIT],
      min_age: 18,
      max_age: 65,
      required_gender: GENDER_UNSPECIFIED,  // 不限制性别
      error_message: "请输入有效的18位身份证号"
    }
  }];
}

验证内容:

  • 长度验证(15位/18位)
  • 校验位算法验证
  • 出生日期有效性
  • 地区码有效性
  • 年龄范围限制
  • 性别限制(可选)

统一社会信用代码验证

message USCCExample {
  string uscc = 1 [(kuban.api.validate.field).string = {
    (kuban.api.validate.uscc_rules): {
      enabled: true,
      verify_checksum: true,
      allowed_types: [
        ORGANIZATION_TYPE_ENTERPRISE,
        ORGANIZATION_TYPE_INSTITUTION
      ],
      error_message: "请输入有效的统一社会信用代码"
    }
  }];
}

银行卡号验证

message BankCardExample {
  string bank_card = 1 [(kuban.api.validate.field).string = {
    (kuban.api.validate.bank_card_rules): {
      enabled: true,
      verify_luhn: true,  // Luhn 算法校验
      allowed_types: [
        BANK_CARD_TYPE_DEBIT,
        BANK_CARD_TYPE_CREDIT
      ],
      allowed_banks: ["ICBC", "CCB", "ABC"],  // 允许的银行
      error_message: "请输入有效的银行卡号"
    }
  }];
}

中文姓名验证

message NameExample {
  string name = 1 [(kuban.api.validate.field).string = {
    (kuban.api.validate.chinese_name_rules): {
      enabled: true,
      min_length: 2,
      max_length: 20,
      allow_ethnic_minority: true,  // 允许少数民族姓名(如阿依·古丽)
      allow_english: false,
      error_message: "请输入2-20个字符的中文姓名"
    }
  }];
}

邮政编码验证

message PostcodeExample {
  string postcode = 1 [(kuban.api.validate.field).string = {
    (kuban.api.validate.postcode_rules): {
      enabled: true,
      country_code: "CN",
      allowed_provinces: ["11", "31", "44"],  // 北京、上海、广东
      error_message: "请输入有效的邮政编码"
    }
  }];
}

分组验证

基本用法

message User {
  // 在创建和更新时都验证
  string username = 1 [(kuban.api.validate.field) = {
    required: true,
    groups: ["create", "update"],
    string: { min_len: 3, max_len: 20 }
  }];

  // 仅在创建时验证
  string password = 2 [(kuban.api.validate.field) = {
    required: true,
    groups: ["create"],
    string: { min_len: 8 }
  }];

  // 仅在修改密码时验证
  string new_password = 3 [(kuban.api.validate.field) = {
    required: true,
    groups: ["change_password"],
    string: { min_len: 8 }
  }];
}

常用验证组

框架预定义了以下常用验证组:

CRUD 操作:

  • create - 创建场景
  • update - 更新场景
  • patch - 部分更新场景
  • delete - 删除场景
  • query - 查询场景

严格程度:

  • strict - 严格模式
  • normal - 标准模式
  • loose - 宽松模式

用户场景:

  • register - 用户注册
  • login - 用户登录
  • profile - 个人资料
  • change_password - 修改密码
  • reset_password - 重置密码

审核场景:

  • draft - 草稿
  • submit - 提交审核
  • approve - 审核通过
  • reject - 审核拒绝

认证场景:

  • real_name_auth - 实名认证
  • enterprise_auth - 企业认证
  • bank_card_binding - 银行卡绑定

安全场景:

  • sensitive_op - 敏感操作
  • financial - 金融交易
  • admin - 管理员操作

验证上下文

在运行时,你可以通过 ValidationContext 指定激活的验证组:

message ValidationContext {
  repeated string active_groups = 1;  // ["create", "strict"]
  repeated string user_roles = 2;
  bool is_admin = 6;
  string language = 9;  // "zh-CN" 或 "en-US"
}

忽略策略

控制何时跳过验证:

message IgnoreExample {
  // 未设置时不验证(默认行为)
  optional string url = 1 [(kuban.api.validate.field) = {
    ignore: IGNORE_UNSPECIFIED,
    string: { uri: true }
  }];

  // 为零值时不验证
  optional string optional_url = 2 [(kuban.api.validate.field) = {
    ignore: IGNORE_IF_ZERO,
    string: { uri: true }
  }];

  // 始终验证
  optional string required_field = 3 [(kuban.api.validate.field) = {
    ignore: IGNORE_NEVER,
    required: true
  }];

  // 始终忽略(用于临时禁用)
  string temp_field = 4 [(kuban.api.validate.field) = {
    ignore: IGNORE_ALWAYS,
    string: { min_len: 10 }
  }];
}

完整示例

用户注册

message UserRegistrationRequest {
  option (kuban.api.validate.message) = {
    groups: ["register"]
  };

  string username = 1 [(kuban.api.validate.field) = {
    required: true,
    groups: ["register"],
    string: {
      min_len: 3,
      max_len: 20,
      pattern: "^[a-zA-Z0-9_]+$"
    },
    cel: [{
      id: "username.reserved",
      message: "该用户名为系统保留,不可使用",
      expression: "!['admin', 'root'].contains(this.toLowerCase())"
    }]
  }];

  string password = 2 [(kuban.api.validate.field) = {
    required: true,
    groups: ["register"],
    string: { min_len: 8, max_len: 32 },
    cel: [{
      id: "password.complexity",
      message: "密码必须包含大小写字母、数字和特殊字符",
      expression: "this.matches('(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d)(?=.*[@$!%*?&])')"
    }]
  }];

  string mobile = 3 [(kuban.api.validate.field) = {
    required: true,
    groups: ["register"],
    string: { chinese_mobile: true }
  }];

  string verification_code = 4 [(kuban.api.validate.field) = {
    required: true,
    groups: ["register"],
    string: { len: 6, numeric: true }
  }];

  optional string email = 5 [(kuban.api.validate.field) = {
    groups: ["register"],
    ignore: IGNORE_IF_ZERO,
    string: { email: true }
  }];

  bool agree_terms = 6 [(kuban.api.validate.field) = {
    required: true,
    groups: ["register"],
    bool: { const: true }
  }];
}

企业认证

message EnterpriseAuthRequest {
  option (kuban.api.validate.message) = {
    groups: ["enterprise_auth"]
  };

  string company_name = 1 [(kuban.api.validate.field) = {
    required: true,
    groups: ["enterprise_auth"],
    string: { min_len: 2, max_len: 100 }
  }];

  string uscc = 2 [(kuban.api.validate.field) = {
    required: true,
    groups: ["enterprise_auth"],
    string: {
      len: 18,
      (kuban.api.validate.uscc_rules): {
        enabled: true,
        verify_checksum: true,
        allowed_types: [ORGANIZATION_TYPE_ENTERPRISE]
      }
    }
  }];

  string legal_person = 3 [(kuban.api.validate.field) = {
    required: true,
    groups: ["enterprise_auth"],
    string: {
      chinese_name: true,
      (kuban.api.validate.chinese_name_rules): {
        enabled: true,
        min_length: 2,
        max_length: 20
      }
    }
  }];

  string legal_person_id_card = 4 [(kuban.api.validate.field) = {
    required: true,
    groups: ["enterprise_auth"],
    string: {
      chinese_id_card: true,
      (kuban.api.validate.chinese_id_card_rules): {
        enabled: true,
        verify_checksum: true,
        min_age: 18
      }
    }
  }];
}

最佳实践

1. 优先使用内置规则

// ✅ 好 - 使用内置规则
string email = 1 [(kuban.api.validate.field).string.email = true];

// ❌ 不推荐 - 自己写正则
string email = 2 [(kuban.api.validate.field).string = {
  pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
}];

2. 合理使用验证组

// ✅ 好 - 根据场景使用不同规则
message User {
  string password = 1 [(kuban.api.validate.field) = {
    groups: ["create", "change_password"],
    required: true,
    string: { min_len: 8 }
  }];
}

// ❌ 不推荐 - 所有场景都验证密码
message User {
  string password = 1 [(kuban.api.validate.field) = {
    required: true,  // 更新用户资料时也要求密码
    string: { min_len: 8 }
  }];
}

3. 提供友好的错误消息

// ✅ 好 - 提供清晰的错误消息
string mobile = 1 [(kuban.api.validate.field).string = {
  (kuban.api.validate.chinese_mobile_rules): {
    enabled: true,
    error_message: "请输入有效的11位手机号"
  }
}];

// ❌ 不推荐 - 使用默认消息
string mobile = 2 [(kuban.api.validate.field).string.chinese_mobile = true];

4. 适当使用 CEL

// ✅ 好 - 复杂逻辑使用 CEL
message DateRange {
  option (kuban.api.validate.message).cel = [{
    id: "date_range.valid",
    message: "结束日期必须大于开始日期",
    expression: "this.end_date > this.start_date"
  }];

  int64 start_date = 1;
  int64 end_date = 2;
}

// ❌ 不推荐 - 简单验证使用 CEL
string email = 1 [(kuban.api.validate.field).cel = [{
  expression: "this.contains('@')"  // 应该用 string.email = true
}]];

5. 使用忽略策略优化可选字段

// ✅ 好 - 可选字段使用 IGNORE_IF_ZERO
optional string website = 1 [(kuban.api.validate.field) = {
  ignore: IGNORE_IF_ZERO,
  string: { uri: true }
}];

// ❌ 不推荐 - 不必要的 required
optional string website = 2 [(kuban.api.validate.field) = {
  required: true,  // optional 和 required 矛盾
  string: { uri: true }
}];

文件结构

proto/kuban/api/
├── validate.proto           # 核心验证框架
├── business_rules.proto     # 业务规则库
├── rule_groups.proto        # 规则分组管理
├── validate_example.proto   # 使用示例
└── README_VALIDATE.md       # 本文档

与 buf/validate 的对比

特性 buf/validate kuban.api.validate
基本验证
CEL 支持
中文错误消息
中国业务规则 ✅ (手机号、身份证等)
分组验证
简化 API
双语支持
条件验证 基础 ✅ 增强

常见问题

Q: 如何在代码中使用这些验证规则?

A: 你需要实现一个验证器来解析和执行这些规则。框架只定义了规则的结构,具体的验证逻辑需要在运行时实现。

Q: 可以自定义业务规则吗?

A: 可以!你可以通过 extend StringRules 添加自己的业务规则扩展。

Q: 验证组在运行时如何激活?

A: 通过 ValidationContext 消息传递激活的组名列表。

Q: CEL 表达式可以访问哪些变量?

A:

  • this - 当前字段的值
  • this.field_name - 同一消息的其他字段(跨字段验证)
  • 内置函数如 size(), matches(), contains()

Q: 如何处理验证错误?

A: 验证器应该返回 ValidationResult 消息,包含所有的 ValidationError 详情。

贡献

欢迎提交 Issue 和 Pull Request 来改进这个验证框架!

许可证

Copyright 2025 Kuban Technologies