|
|
1 bulan lalu | |
|---|---|---|
| .. | ||
| README_VALIDATE.md | 1 bulan lalu | |
| business_rules.proto | 1 bulan lalu | |
| rule_groups.proto | 1 bulan lalu | |
| validate.proto | 1 bulan lalu | |
| validate_example.proto | 1 bulan lalu | |
基于 buf/validate 优化设计的自定义字段验证框架,提供简化的 API、中文支持和业务规则集成。
简化的 API 设计
增强的中文支持
业务规则集成
分组验证
保留 CEL 支持
import "kuban/api/validate.proto";
import "kuban/api/business_rules.proto"; // 可选:业务规则
import "kuban/api/rule_groups.proto"; // 可选:分组验证
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
}
}];
}
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
}
}];
}
框架支持三个级别的验证:
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 }
}
}];
}
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: "请输入有效的中国大陆手机号"
}
}];
}
支持的运营商:
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位身份证号"
}
}];
}
验证内容:
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
}
}
}];
}
// ✅ 好 - 使用内置规则
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,}$"
}];
// ✅ 好 - 根据场景使用不同规则
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 }
}];
}
// ✅ 好 - 提供清晰的错误消息
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];
// ✅ 好 - 复杂逻辑使用 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
}]];
// ✅ 好 - 可选字段使用 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 | kuban.api.validate |
|---|---|---|
| 基本验证 | ✅ | ✅ |
| CEL 支持 | ✅ | ✅ |
| 中文错误消息 | ❌ | ✅ |
| 中国业务规则 | ❌ | ✅ (手机号、身份证等) |
| 分组验证 | ❌ | ✅ |
| 简化 API | ❌ | ✅ |
| 双语支持 | ❌ | ✅ |
| 条件验证 | 基础 | ✅ 增强 |
A: 你需要实现一个验证器来解析和执行这些规则。框架只定义了规则的结构,具体的验证逻辑需要在运行时实现。
A: 可以!你可以通过 extend StringRules 添加自己的业务规则扩展。
A: 通过 ValidationContext 消息传递激活的组名列表。
A:
this - 当前字段的值this.field_name - 同一消息的其他字段(跨字段验证)size(), matches(), contains() 等A: 验证器应该返回 ValidationResult 消息,包含所有的 ValidationError 详情。
欢迎提交 Issue 和 Pull Request 来改进这个验证框架!
Copyright 2025 Kuban Technologies