数据验证设计思路
目录
概述
数据验证是确保系统数据质量和业务规则合规性的关键组件。在CRUD表单系统中,数据验证接口负责在数据操作的各个环节对用户输入进行校验,防止无效、不完整或不符合业务规则的数据进入系统。
核心目标
- 数据完整性:确保必填字段不为空,数据格式正确
- 业务合规性:验证数据是否符合业务规则和约束
- 用户体验:提供实时反馈,减少用户重复操作
- 系统安全性:防止恶意数据注入和系统攻击
- 维护便利性:提供可配置、可扩展的验证机制
设计原则
1. 分层验证原则
前端验证 → 接口验证 → 业务验证 → 数据库约束
- 前端验证:提供即时用户反馈,改善用户体验
- 接口验证:确保API接口的数据安全性
- 业务验证:执行复杂的业务规则校验
- 数据库约束:最后一道防线,确保数据完整性
2. 可配置性原则
- 验证规则通过配置文件或数据库管理
- 支持动态添加、修改、删除验证规则
- 无需修改代码即可调整验证逻辑
3. 可扩展性原则
- 支持自定义验证器
- 提供插件化的验证机制
- 易于集成第三方验证组件
4. 性能优化原则
- 支持验证规则缓存
- 批量验证优化
- 异步验证处理
5. 错误友好原则
- 提供清晰、具体的错误信息
- 支持多语言错误消息
- 提供修复建议
验证架构
整体架构图
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端验证层 │ │ 接口验证层 │ │ 业务验证层 │
│ │ │ │ │ │
│ • 实时验证 │ │ • 参数校验 │ │ • 业务规则 │
│ • 格式检查 │────▶│ • 数据类型 │────▶│ • 唯一性检查 │
│ • 用户体验 │ │ • 安全过滤 │ │ • 关联验证 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ 数据持久层 │
│ │
│ • 数据库约束 │
│ • 外键检查 │
│ • 触发器验证 │
└─────────────────┘
核心组件
- 验证控制器(ValidationController)
- 提供统一的验证接口入口
- 处理验证请求和响应
- 验证服务(ValidationService)
- 核心验证逻辑实现
- 协调各种验证器的执行
- 验证规则引擎(ValidationRuleEngine)
- 解析和执行验证规则
- 管理验证规则的生命周期
- 验证器工厂(ValidatorFactory)
- 创建和管理各种验证器实例
- 支持验证器的注册和发现
- 规则配置管理器(RuleConfigManager)
- 管理验证规则的配置
- 支持动态加载和更新
验证类型
1. 基础验证
必填验证
@Component
public class RequiredValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
if (value == null || value.toString().trim().isEmpty()) {
return ValidationResult.error("字段不能为空");
}
return ValidationResult.success();
}
}
长度验证
@Component
public class LengthValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
if (value == null) return ValidationResult.success();
String str = value.toString();
Integer minLength = rule.getMinLength();
Integer maxLength = rule.getMaxLength();
if (minLength != null && str.length() < minLength) {
return ValidationResult.error("长度不能少于" + minLength + "个字符");
}
if (maxLength != null && str.length() > maxLength) {
return ValidationResult.error("长度不能超过" + maxLength + "个字符");
}
return ValidationResult.success();
}
}
格式验证
@Component
public class PatternValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
if (value == null) return ValidationResult.success();
String pattern = rule.getPattern();
if (StringUtils.isBlank(pattern)) return ValidationResult.success();
if (!value.toString().matches(pattern)) {
return ValidationResult.error(rule.getErrorMessage());
}
return ValidationResult.success();
}
}
2. 数据类型验证
数字验证
@Component
public class NumberValidator implements FieldValidator {
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
if (value == null) return ValidationResult.success();
try {
Double.parseDouble(value.toString());
return ValidationResult.success();
} catch (NumberFormatException e) {
return ValidationResult.error("必须是有效的数字");
}
}
}
日期验证
@Component
public class DateValidator implements FieldValidator {
private static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
if (value == null) return ValidationResult.success();
String datePattern = rule.getDatePattern();
if (StringUtils.isBlank(datePattern)) {
datePattern = DEFAULT_DATE_PATTERN;
}
try {
SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
sdf.setLenient(false);
sdf.parse(value.toString());
return ValidationResult.success();
} catch (ParseException e) {
return ValidationResult.error("日期格式不正确,应为:" + datePattern);
}
}
}
3. 业务验证
唯一性验证
@Component
public class UniquenessValidator implements BusinessValidator {
@Autowired
private CrudFormsService crudFormsService;
@Override
public ValidationResult validate(String uri, String fieldName, Object value, Object recordId) {
if (value == null) return ValidationResult.success();
// 构建查询条件
JSONObject params = new JSONObject();
params.put(fieldName, value);
try {
List<HashMap<String, Object>> existingRecords =
crudFormsService.processSelectDynamicSql(uri, params);
if (!existingRecords.isEmpty()) {
// 更新操作时排除当前记录
if (recordId != null) {
existingRecords = existingRecords.stream()
.filter(record -> !recordId.equals(record.get("id")))
.collect(Collectors.toList());
}
if (!existingRecords.isEmpty()) {
return ValidationResult.error(fieldName + "的值已存在,请使用其他值");
}
}
return ValidationResult.success();
} catch (Exception e) {
return ValidationResult.error("唯一性验证失败:" + e.getMessage());
}
}
}
关联验证
@Component
public class ReferenceValidator implements BusinessValidator {
@Override
public ValidationResult validate(String uri, String fieldName, Object value, Map<String, Object> config) {
if (value == null) return ValidationResult.success();
String referenceTable = (String) config.get("referenceTable");
String referenceField = (String) config.get("referenceField");
// 验证关联数据是否存在
// 实现具体的关联验证逻辑
return ValidationResult.success();
}
}
4. 自定义验证
/**
* 项目编码验证器示例
*/
@Component("projectCodeValidator")
public class ProjectCodeValidator implements CustomValidator {
@Override
public ValidationResult validate(Object value, Map<String, Object> context) {
if (value == null) return ValidationResult.success();
String projectCode = value.toString();
// 项目编码格式:PRJ + 年份 + 4位序号
String pattern = "^PRJ\\d{4}\\d{4}$";
if (!projectCode.matches(pattern)) {
return ValidationResult.error("项目编码格式不正确,应为:PRJ + 年份 + 4位序号");
}
// 验证年份合理性
String yearStr = projectCode.substring(3, 7);
int year = Integer.parseInt(yearStr);
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
if (year < 2020 || year > currentYear + 1) {
return ValidationResult.error("项目编码中的年份不合理");
}
return ValidationResult.success();
}
}
技术实现
1. 验证服务核心实现
@Service
public class DataValidationServiceImpl implements DataValidationService {
@Autowired
private ValidationRuleEngine ruleEngine;
@Autowired
private ValidatorFactory validatorFactory;
@Autowired
private RuleConfigManager ruleConfigManager;
@Override
public FieldValidationResult validateField(FieldValidationRequest request) {
FieldValidationResult result = new FieldValidationResult();
try {
// 1. 获取验证规则
List<ValidationRule> rules = ruleConfigManager.getFieldRules(
request.getUri(), request.getFieldName());
// 2. 执行验证
for (ValidationRule rule : rules) {
if (!rule.isEnabled()) continue;
FieldValidator validator = validatorFactory.getValidator(rule.getRuleType());
ValidationResult validationResult = validator.validate(request.getFieldValue(), rule);
if (!validationResult.isValid()) {
result.addError(validationResult.getErrorMessage());
}
}
// 3. 业务验证
if (result.isValid()) {
executeBusinessValidation(request, result);
}
} catch (Exception e) {
result.setValid(false);
result.addError("验证过程发生异常: " + e.getMessage());
log.error("字段验证异常", e);
}
return result;
}
@Override
public FormValidationResult validateForm(FormValidationRequest request) {
FormValidationResult result = new FormValidationResult();
Map<String, FieldValidationResult> fieldResults = new HashMap<>();
// 1. 逐字段验证
for (Map.Entry<String, Object> entry : request.getFormData().entrySet()) {
FieldValidationRequest fieldRequest = new FieldValidationRequest();
fieldRequest.setUri(request.getUri());
fieldRequest.setFieldName(entry.getKey());
fieldRequest.setFieldValue(entry.getValue());
fieldRequest.setContext(buildContext(request));
FieldValidationResult fieldResult = validateField(fieldRequest);
fieldResults.put(entry.getKey(), fieldResult);
if (!fieldResult.isValid()) {
result.setValid(false);
}
}
result.setFieldResults(fieldResults);
// 2. 表单级业务验证
if (result.isValid()) {
executeFormBusinessValidation(request, result);
}
return result;
}
}
2. 验证规则引擎
@Component
public class ValidationRuleEngine {
private final Map<String, FieldValidator> validators = new ConcurrentHashMap<>();
private final Map<String, BusinessValidator> businessValidators = new ConcurrentHashMap<>();
@PostConstruct
public void initValidators() {
// 注册基础验证器
registerValidator("REQUIRED", new RequiredValidator());
registerValidator("LENGTH", new LengthValidator());
registerValidator("PATTERN", new PatternValidator());
registerValidator("NUMBER", new NumberValidator());
registerValidator("DATE", new DateValidator());
registerValidator("EMAIL", new EmailValidator());
registerValidator("PHONE", new PhoneValidator());
// 注册业务验证器
registerBusinessValidator("UNIQUENESS", new UniquenessValidator());
registerBusinessValidator("REFERENCE", new ReferenceValidator());
registerBusinessValidator("RANGE", new RangeValidator());
}
public void registerValidator(String type, FieldValidator validator) {
validators.put(type, validator);
}
public void registerBusinessValidator(String type, BusinessValidator validator) {
businessValidators.put(type, validator);
}
public FieldValidator getValidator(String type) {
return validators.get(type);
}
public BusinessValidator getBusinessValidator(String type) {
return businessValidators.get(type);
}
}
3. 验证器工厂
@Component
public class ValidatorFactory {
@Autowired
private ApplicationContext applicationContext;
private final Map<String, FieldValidator> validatorCache = new ConcurrentHashMap<>();
public FieldValidator getValidator(String validatorType) {
return validatorCache.computeIfAbsent(validatorType, type -> {
try {
// 首先尝试从Spring容器获取
return applicationContext.getBean(type, FieldValidator.class);
} catch (NoSuchBeanDefinitionException e) {
// 如果容器中没有,尝试动态创建
return createValidator(type);
}
});
}
private FieldValidator createValidator(String type) {
switch (type.toUpperCase()) {
case "REQUIRED":
return new RequiredValidator();
case "LENGTH":
return new LengthValidator();
case "PATTERN":
return new PatternValidator();
case "NUMBER":
return new NumberValidator();
case "DATE":
return new DateValidator();
case "EMAIL":
return new EmailValidator();
case "PHONE":
return new PhoneValidator();
default:
throw new IllegalArgumentException("未知的验证器类型: " + type);
}
}
}
接口设计
1. REST API 接口
@RestController
@RequestMapping("/api/validation")
@Api(tags = "数据验证接口")
public class DataValidationController {
@Autowired
private DataValidationService validationService;
/**
* 字段验证接口
*/
@PostMapping("/field")
@ApiOperation("字段验证")
public ResponseResult<FieldValidationResult> validateField(
@RequestBody @Valid FieldValidationRequest request) {
FieldValidationResult result = validationService.validateField(request);
return ResponseResult.success(result);
}
/**
* 表单验证接口
*/
@PostMapping("/form")
@ApiOperation("表单验证")
public ResponseResult<FormValidationResult> validateForm(
@RequestBody @Valid FormValidationRequest request) {
FormValidationResult result = validationService.validateForm(request);
return ResponseResult.success(result);
}
/**
* 批量验证接口
*/
@PostMapping("/batch")
@ApiOperation("批量验证")
public ResponseResult<BatchValidationResult> validateBatch(
@RequestBody @Valid BatchValidationRequest request) {
BatchValidationResult result = validationService.validateBatch(request);
return ResponseResult.success(result);
}
/**
* 获取验证规则接口
*/
@GetMapping("/rules/{uri}")
@ApiOperation("获取表单验证规则")
public ResponseResult<List<ValidationRule>> getValidationRules(
@PathVariable String uri) {
List<ValidationRule> rules = validationService.getValidationRules(uri);
return ResponseResult.success(rules);
}
}
2. 请求响应数据结构
/**
* 字段验证请求
*/
@Data
@ApiModel("字段验证请求")
public class FieldValidationRequest {
@ApiModelProperty("表单URI")
@NotBlank(message = "表单URI不能为空")
private String uri;
@ApiModelProperty("字段名")
@NotBlank(message = "字段名不能为空")
private String fieldName;
@ApiModelProperty("字段值")
private Object fieldValue;
@ApiModelProperty("验证类型")
private String validationType;
@ApiModelProperty("上下文信息")
private Map<String, Object> context;
}
/**
* 字段验证结果
*/
@Data
@ApiModel("字段验证结果")
public class FieldValidationResult {
@ApiModelProperty("是否验证通过")
private boolean valid = true;
@ApiModelProperty("错误信息列表")
private List<String> errors;
@ApiModelProperty("警告信息列表")
private List<String> warnings;
@ApiModelProperty("建议信息")
private Map<String, Object> suggestions;
public void addError(String error) {
if (errors == null) errors = new ArrayList<>();
errors.add(error);
this.valid = false;
}
public void addWarning(String warning) {
if (warnings == null) warnings = new ArrayList<>();
warnings.add(warning);
}
}
/**
* 表单验证请求
*/
@Data
@ApiModel("表单验证请求")
public class FormValidationRequest {
@ApiModelProperty("表单URI")
@NotBlank(message = "表单URI不能为空")
private String uri;
@ApiModelProperty("表单数据")
@NotNull(message = "表单数据不能为空")
private Map<String, Object> formData;
@ApiModelProperty("操作类型")
private String operation; // INSERT, UPDATE, DELETE
@ApiModelProperty("记录ID")
private Object recordId;
@ApiModelProperty("验证组")
private String validationGroup;
}
/**
* 表单验证结果
*/
@Data
@ApiModel("表单验证结果")
public class FormValidationResult {
@ApiModelProperty("整体是否验证通过")
private boolean valid = true;
@ApiModelProperty("各字段验证结果")
private Map<String, FieldValidationResult> fieldResults;
@ApiModelProperty("全局错误信息")
private List<String> globalErrors;
@ApiModelProperty("业务规则错误")
private List<String> businessErrors;
public void addGlobalError(String error) {
if (globalErrors == null) globalErrors = new ArrayList<>();
globalErrors.add(error);
this.valid = false;
}
public void addBusinessError(String error) {
if (businessErrors == null) businessErrors = new ArrayList<>();
businessErrors.add(error);
this.valid = false;
}
}
配置管理
1. 数据库表设计
-- 验证规则配置表
CREATE TABLE validation_rules (
rule_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '规则ID',
uri VARCHAR(100) NOT NULL COMMENT '表单URI',
field_name VARCHAR(100) NOT NULL COMMENT '字段名',
rule_type VARCHAR(50) NOT NULL COMMENT '规则类型',
rule_expression TEXT COMMENT '规则表达式',
error_message VARCHAR(500) COMMENT '错误信息',
is_required TINYINT(1) DEFAULT 0 COMMENT '是否必填',
min_length INT COMMENT '最小长度',
max_length INT COMMENT '最大长度',
min_value DECIMAL(20,6) COMMENT '最小值',
max_value DECIMAL(20,6) COMMENT '最大值',
pattern VARCHAR(500) COMMENT '正则表达式',
allowed_values TEXT COMMENT '允许的值列表(JSON)',
custom_validator VARCHAR(200) COMMENT '自定义验证器类名',
validation_group VARCHAR(50) COMMENT '验证组',
enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
sort_order INT DEFAULT 0 COMMENT '排序',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_uri_field (uri, field_name),
INDEX idx_enabled (enabled)
) COMMENT='验证规则配置表';
-- 业务规则配置表
CREATE TABLE business_rules (
rule_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '规则ID',
uri VARCHAR(100) NOT NULL COMMENT '表单URI',
rule_name VARCHAR(100) NOT NULL COMMENT '规则名称',
rule_type VARCHAR(50) NOT NULL COMMENT '规则类型',
rule_config TEXT COMMENT '规则配置(JSON)',
error_message VARCHAR(500) COMMENT '错误信息',
enabled TINYINT(1) DEFAULT 1 COMMENT '是否启用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
INDEX idx_uri (uri),
INDEX idx_enabled (enabled)
) COMMENT='业务规则配置表';
-- 验证规则缓存表
CREATE TABLE validation_rule_cache (
cache_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '缓存ID',
cache_key VARCHAR(200) NOT NULL COMMENT '缓存键',
cache_value TEXT NOT NULL COMMENT '缓存值(JSON)',
expire_time DATETIME COMMENT '过期时间',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE KEY uk_cache_key (cache_key),
INDEX idx_expire_time (expire_time)
) COMMENT='验证规则缓存表';
2. 配置文件管理
# application.yml
validation:
# 验证配置
config:
# 是否启用缓存
cache-enabled: true
# 缓存过期时间(秒)
cache-expire-time: 3600
# 是否启用异步验证
async-enabled: false
# 批量验证最大数量
batch-max-size: 1000
# 默认验证规则
default-rules:
# 字符串字段默认最大长度
string-max-length: 255
# 数字字段默认精度
number-precision: 2
# 日期字段默认格式
date-format: "yyyy-MM-dd"
# 错误消息配置
messages:
required: "{0}不能为空"
length: "{0}长度必须在{1}-{2}个字符之间"
pattern: "{0}格式不正确"
number: "{0}必须是有效的数字"
date: "{0}日期格式不正确"
email: "{0}邮箱格式不正确"
phone: "{0}手机号格式不正确"
uniqueness: "{0}的值已存在"
reference: "{0}引用的数据不存在"
3. 规则配置管理器
@Component
public class RuleConfigManager {
@Autowired
private ValidationRuleMapper validationRuleMapper;
@Autowired
private BusinessRuleMapper businessRuleMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Value("${validation.config.cache-enabled:true}")
private boolean cacheEnabled;
@Value("${validation.config.cache-expire-time:3600}")
private long cacheExpireTime;
/**
* 获取字段验证规则
*/
public List<ValidationRule> getFieldRules(String uri, String fieldName) {
String cacheKey = "validation:field:" + uri + ":" + fieldName;
if (cacheEnabled) {
List<ValidationRule> cachedRules = getCachedRules(cacheKey);
if (cachedRules != null) {
return cachedRules;
}
}
List<ValidationRule> rules = validationRuleMapper.selectByUriAndField(uri, fieldName);
if (cacheEnabled && !rules.isEmpty()) {
cacheRules(cacheKey, rules);
}
return rules;
}
/**
* 获取表单所有验证规则
*/
public Map<String, List<ValidationRule>> getFormRules(String uri) {
String cacheKey = "validation:form:" + uri;
if (cacheEnabled) {
Map<String, List<ValidationRule>> cachedRules = getCachedFormRules(cacheKey);
if (cachedRules != null) {
return cachedRules;
}
}
List<ValidationRule> allRules = validationRuleMapper.selectByUri(uri);
Map<String, List<ValidationRule>> formRules = allRules.stream()
.collect(Collectors.groupingBy(ValidationRule::getFieldName));
if (cacheEnabled && !formRules.isEmpty()) {
cacheFormRules(cacheKey, formRules);
}
return formRules;
}
/**
* 获取业务规则
*/
public List<BusinessRule> getBusinessRules(String uri) {
String cacheKey = "validation:business:" + uri;
if (cacheEnabled) {
List<BusinessRule> cachedRules = getCachedBusinessRules(cacheKey);
if (cachedRules != null) {
return cachedRules;
}
}
List<BusinessRule> rules = businessRuleMapper.selectByUri(uri);
if (cacheEnabled && !rules.isEmpty()) {
cacheBusinessRules(cacheKey, rules);
}
return rules;
}
/**
* 清除缓存
*/
public void clearCache(String uri) {
if (!cacheEnabled) return;
Set<String> keys = redisTemplate.keys("validation:*:" + uri + "*");
if (!keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
/**
* 重新加载规则
*/
public void reloadRules(String uri) {
clearCache(uri);
// 预加载规则到缓存
getFormRules(uri);
getBusinessRules(uri);
}
}
使用示例
1. 前端集成示例
// 实时字段验证
class FieldValidator {
constructor(uri) {
this.uri = uri;
this.debounceTime = 300;
}
// 防抖验证
validateField = debounce(async (fieldName, fieldValue) => {
try {
const response = await fetch('/api/validation/field', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: this.uri,
fieldName: fieldName,
fieldValue: fieldValue
})
});
const result = await response.json();
this.handleValidationResult(fieldName, result.data);
} catch (error) {
console.error('验证请求失败:', error);
}
}, this.debounceTime);
// 处理验证结果
handleValidationResult(fieldName, result) {
const fieldElement = document.querySelector(`[name="${fieldName}"]`);
const errorElement = document.querySelector(`#${fieldName}-error`);
if (result.valid) {
fieldElement.classList.remove('error');
errorElement.textContent = '';
} else {
fieldElement.classList.add('error');
errorElement.textContent = result.errors.join(', ');
}
}
// 表单提交验证
async validateForm(formData) {
try {
const response = await fetch('/api/validation/form', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
uri: this.uri,
formData: formData,
operation: 'INSERT'
})
});
const result = await response.json();
return this.handleFormValidationResult(result.data);
} catch (error) {
console.error('表单验证失败:', error);
return false;
}
}
// 处理表单验证结果
handleFormValidationResult(result) {
if (result.valid) {
this.clearAllErrors();
return true;
}
// 显示字段错误
if (result.fieldResults) {
Object.entries(result.fieldResults).forEach(([fieldName, fieldResult]) => {
if (!fieldResult.valid) {
this.showFieldError(fieldName, fieldResult.errors);
}
});
}
// 显示全局错误
if (result.globalErrors && result.globalErrors.length > 0) {
this.showGlobalErrors(result.globalErrors);
}
return false;
}
}
// 使用示例
const validator = new FieldValidator('userInfo');
// 绑定字段验证事件
document.querySelectorAll('input, select, textarea').forEach(element => {
element.addEventListener('blur', (event) => {
validator.validateField(event.target.name, event.target.value);
});
});
// 表单提交验证
document.getElementById('submitBtn').addEventListener('click', async (event) => {
event.preventDefault();
const formData = new FormData(document.getElementById('userForm'));
const data = Object.fromEntries(formData.entries());
const isValid = await validator.validateForm(data);
if (isValid) {
// 提交表单数据
submitFormData(data);
}
});
2. Vue.js 集成示例
<template>
<el-form ref="formRef" :model="formData" :rules="validationRules">
<el-form-item label="用户名" prop="username">
<el-input
v-model="formData.username"
@blur="validateField('username')"
:class="{ 'error': fieldErrors.username }"
/>
<div v-if="fieldErrors.username" class="error-message">
{{ fieldErrors.username }}
</div>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input
v-model="formData.email"
@blur="validateField('email')"
:class="{ 'error': fieldErrors.email }"
/>
<div v-if="fieldErrors.email" class="error-message">
{{ fieldErrors.email }}
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
</el-form-item>
</el-form>
</template>
<script>
import { debounce } from 'lodash';
export default {
name: 'UserForm',
data() {
return {
uri: 'userInfo',
formData: {
username: '',
email: ''
},
fieldErrors: {},
validationRules: {}
};
},
mounted() {
this.loadValidationRules();
},
methods: {
// 加载验证规则
async loadValidationRules() {
try {
const response = await this.$http.get(`/api/validation/rules/${this.uri}`);
this.buildElementUIRules(response.data);
} catch (error) {
console.error('加载验证规则失败:', error);
}
},
// 构建Element UI验证规则
buildElementUIRules(rules) {
const elementRules = {};
rules.forEach(rule => {
if (!elementRules[rule.fieldName]) {
elementRules[rule.fieldName] = [];
}
const elementRule = {
message: rule.errorMessage,
trigger: 'blur'
};
switch (rule.ruleType) {
case 'REQUIRED':
elementRule.required = true;
break;
case 'LENGTH':
elementRule.min = rule.minLength;
elementRule.max = rule.maxLength;
break;
case 'PATTERN':
elementRule.pattern = new RegExp(rule.pattern);
break;
}
elementRules[rule.fieldName].push(elementRule);
});
this.validationRules = elementRules;
},
// 字段验证(防抖)
validateField: debounce(async function(fieldName) {
try {
const response = await this.$http.post('/api/validation/field', {
uri: this.uri,
fieldName: fieldName,
fieldValue: this.formData[fieldName]
});
const result = response.data;
if (result.valid) {
this.$delete(this.fieldErrors, fieldName);
} else {
this.$set(this.fieldErrors, fieldName, result.errors.join(', '));
}
} catch (error) {
console.error('字段验证失败:', error);
}
}, 300),
// 表单提交
async submitForm() {
try {
// 先进行Element UI验证
const valid = await this.$refs.formRef.validate();
if (!valid) return;
// 再进行服务端验证
const response = await this.$http.post('/api/validation/form', {
uri: this.uri,
formData: this.formData,
operation: 'INSERT'
});
const result = response.data;
if (result.valid) {
// 提交数据
await this.submitData();
this.$message.success('提交成功');
} else {
this.handleValidationErrors(result);
}
} catch (error) {
console.error('表单提交失败:', error);
this.$message.error('提交失败');
}
},
// 处理验证错误
handleValidationErrors(result) {
// 清除之前的错误
this.fieldErrors = {};
// 显示字段错误
if (result.fieldResults) {
Object.entries(result.fieldResults).forEach(([fieldName, fieldResult]) => {
if (!fieldResult.valid) {
this.$set(this.fieldErrors, fieldName, fieldResult.errors.join(', '));
}
});
}
// 显示全局错误
if (result.globalErrors && result.globalErrors.length > 0) {
this.$message.error(result.globalErrors.join(', '));
}
},
// 提交数据
async submitData() {
const response = await this.$http.post(`/api/dataManager/insert/${this.uri}`, this.formData);
return response.data;
}
}
};
</script>
<style scoped>
.error {
border-color: #f56c6c !important;
}
.error-message {
color: #f56c6c;
font-size: 12px;
margin-top: 4px;
}
</style>
3. 后端集成示例
/**
* 在现有CRUD控制器中集成验证
*/
@RestController
@RequestMapping("/api")
public class CurdFormsController {
@Autowired
private DataValidationService validationService;
@Autowired
private CrudFormsService crudFormsService;
/**
* 插入数据(集成验证)
*/
@PostMapping("/dataManager/insert/{uri}")
public ResponseResult<Boolean> processInsertDynamicSql(
@PathVariable String uri,
@RequestBody JSONObject params) {
// 1. 数据验证
FormValidationRequest validationRequest = new FormValidationRequest();
validationRequest.setUri(uri);
validationRequest.setFormData(params);
validationRequest.setOperation("INSERT");
FormValidationResult validationResult = validationService.validateForm(validationRequest);
// 2. 验证失败时返回详细错误信息
if (!validationResult.isValid()) {
return ResponseResult.fail(1010, "数据验证失败", validationResult);
}
// 3. 验证通过后执行插入
try {
int result = crudFormsService.processInsertDynamicSql(uri, params);
return ResponseResult.success(result > 0);
} catch (Exception e) {
log.error("数据插入失败", e);
return ResponseResult.fail(1001, "数据插入失败: " + e.getMessage());
}
}
/**
* 更新数据(集成验证)
*/
@PostMapping("/dataManager/update/{uri}")
public ResponseResult<Boolean> processUpdateDynamicSql(
@PathVariable String uri,
@RequestBody JSONObject params) {
// 1. 数据验证
FormValidationRequest validationRequest = new FormValidationRequest();
validationRequest.setUri(uri);
validationRequest.setFormData(params);
validationRequest.setOperation("UPDATE");
validationRequest.setRecordId(params.get("id")); // 假设主键字段为id
FormValidationResult validationResult = validationService.validateForm(validationRequest);
// 2. 验证失败时返回详细错误信息
if (!validationResult.isValid()) {
return ResponseResult.fail(1010, "数据验证失败", validationResult);
}
// 3. 验证通过后执行更新
try {
int result = crudFormsService.processUpdateDynamicSql(uri, params);
return ResponseResult.success(result > 0);
} catch (Exception e) {
log.error("数据更新失败", e);
return ResponseResult.fail(1002, "数据更新失败: " + e.getMessage());
}
}
/**
* 批量操作验证
*/
@PostMapping("/dataManager/batch-insert/{uri}")
public ResponseResult<BatchOperationResult> processBatchInsert(
@PathVariable String uri,
@RequestBody List<JSONObject> dataList) {
// 1. 批量验证
BatchValidationRequest validationRequest = new BatchValidationRequest();
validationRequest.setUri(uri);
validationRequest.setDataList(dataList);
validationRequest.setOperation("INSERT");
BatchValidationResult validationResult = validationService.validateBatch(validationRequest);
// 2. 处理验证结果
BatchOperationResult operationResult = new BatchOperationResult();
if (validationResult.hasErrors()) {
// 有验证错误,返回错误详情
operationResult.setSuccess(false);
operationResult.setValidationErrors(validationResult.getErrors());
return ResponseResult.fail(1010, "批量数据验证失败", operationResult);
}
// 3. 验证通过,执行批量插入
try {
List<Integer> results = new ArrayList<>();
for (JSONObject data : dataList) {
int result = crudFormsService.processInsertDynamicSql(uri, data);
results.add(result);
}
operationResult.setSuccess(true);
operationResult.setResults(results);
operationResult.setTotalCount(dataList.size());
operationResult.setSuccessCount(results.size());
return ResponseResult.success(operationResult);
} catch (Exception e) {
log.error("批量插入失败", e);
operationResult.setSuccess(false);
operationResult.setErrorMessage("批量插入失败: " + e.getMessage());
return ResponseResult.fail(1003, "批量插入失败", operationResult);
}
}
}
最佳实践
1. 验证规则设计原则
分层验证
前端验证(用户体验)
↓
接口验证(数据安全)
↓
业务验证(业务规则)
↓
数据库约束(数据完整性)
验证规则优先级
- 必填验证 - 最高优先级
- 格式验证 - 数据类型和格式
- 长度验证 - 字符串长度限制
- 范围验证 - 数值范围限制
- 业务验证 - 唯一性、关联性等
- 自定义验证 - 复杂业务逻辑
2. 性能优化策略
缓存策略
@Service
public class ValidationCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String CACHE_PREFIX = "validation:";
private static final long DEFAULT_EXPIRE_TIME = 3600; // 1小时
/**
* 缓存验证规则
*/
public void cacheValidationRules(String uri, List<ValidationRule> rules) {
String cacheKey = CACHE_PREFIX + "rules:" + uri;
redisTemplate.opsForValue().set(cacheKey, rules, DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
}
/**
* 获取缓存的验证规则
*/
@SuppressWarnings("unchecked")
public List<ValidationRule> getCachedValidationRules(String uri) {
String cacheKey = CACHE_PREFIX + "rules:" + uri;
return (List<ValidationRule>) redisTemplate.opsForValue().get(cacheKey);
}
/**
* 缓存验证结果(短期缓存)
*/
public void cacheValidationResult(String key, ValidationResult result) {
String cacheKey = CACHE_PREFIX + "result:" + key;
redisTemplate.opsForValue().set(cacheKey, result, 300, TimeUnit.SECONDS); // 5分钟
}
}
异步验证
@Service
public class AsyncValidationService {
@Autowired
private TaskExecutor taskExecutor;
/**
* 异步字段验证
*/
public CompletableFuture<FieldValidationResult> validateFieldAsync(FieldValidationRequest request) {
return CompletableFuture.supplyAsync(() -> {
return validationService.validateField(request);
}, taskExecutor);
}
/**
* 异步批量验证
*/
public CompletableFuture<BatchValidationResult> validateBatchAsync(BatchValidationRequest request) {
return CompletableFuture.supplyAsync(() -> {
return validationService.validateBatch(request);
}, taskExecutor);
}
}
3. 错误处理策略
统一错误响应格式
@Data
public class ValidationError {
private String field; // 字段名
private String code; // 错误代码
private String message; // 错误信息
private Object rejectedValue; // 被拒绝的值
private Map<String, Object> context; // 上下文信息
}
@Data
public class ValidationResponse {
private boolean success;
private List<ValidationError> errors;
private Map<String, Object> metadata;
public static ValidationResponse success() {
ValidationResponse response = new ValidationResponse();
response.setSuccess(true);
return response;
}
public static ValidationResponse fail(List<ValidationError> errors) {
ValidationResponse response = new ValidationResponse();
response.setSuccess(false);
response.setErrors(errors);
return response;
}
}
国际化错误消息
@Component
public class ValidationMessageResolver {
@Autowired
private MessageSource messageSource;
public String resolveMessage(String code, Object[] args, Locale locale) {
try {
return messageSource.getMessage(code, args, locale);
} catch (NoSuchMessageException e) {
return code; // 返回错误代码作为默认消息
}
}
public String resolveFieldMessage(String fieldName, String ruleType, Object[] args, Locale locale) {
String code = "validation." + ruleType.toLowerCase();
Object[] messageArgs = new Object[args.length + 1];
messageArgs[0] = fieldName;
System.arraycopy(args, 0, messageArgs, 1, args.length);
return resolveMessage(code, messageArgs, locale);
}
}
4. 监控和日志
验证性能监控
@Component
@Slf4j
public class ValidationMonitor {
private final MeterRegistry meterRegistry;
private final Timer validationTimer;
private final Counter validationCounter;
public ValidationMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.validationTimer = Timer.builder("validation.duration")
.description("Validation execution time")
.register(meterRegistry);
this.validationCounter = Counter.builder("validation.count")
.description("Validation execution count")
.register(meterRegistry);
}
public <T> T monitorValidation(String operation, Supplier<T> validationSupplier) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
T result = validationSupplier.get();
validationCounter.increment(Tags.of("operation", operation, "result", "success"));
return result;
} catch (Exception e) {
validationCounter.increment(Tags.of("operation", operation, "result", "error"));
throw e;
} finally {
sample.stop(validationTimer.withTags("operation", operation));
}
}
}
验证审计日志
@Component
@Slf4j
public class ValidationAuditLogger {
public void logValidationAttempt(String uri, String operation, String userId, Map<String, Object> data) {
log.info("Validation attempt - URI: {}, Operation: {}, User: {}, Data: {}",
uri, operation, userId, maskSensitiveData(data));
}
public void logValidationResult(String uri, String operation, String userId, boolean success, List<String> errors) {
if (success) {
log.info("Validation success - URI: {}, Operation: {}, User: {}", uri, operation, userId);
} else {
log.warn("Validation failed - URI: {}, Operation: {}, User: {}, Errors: {}",
uri, operation, userId, errors);
}
}
public void logValidationError(String uri, String operation, String userId, Exception error) {
log.error("Validation error - URI: {}, Operation: {}, User: {}, Error: {}",
uri, operation, userId, error.getMessage(), error);
}
private Map<String, Object> maskSensitiveData(Map<String, Object> data) {
// 实现敏感数据脱敏逻辑
Map<String, Object> maskedData = new HashMap<>(data);
maskedData.replaceAll((key, value) -> {
if (isSensitiveField(key)) {
return "***";
}
return value;
});
return maskedData;
}
private boolean isSensitiveField(String fieldName) {
return fieldName.toLowerCase().contains("password")
|| fieldName.toLowerCase().contains("secret")
|| fieldName.toLowerCase().contains("token");
}
}
扩展方案
1. 插件化验证器
/**
* 验证器插件接口
*/
public interface ValidationPlugin {
/**
* 插件名称
*/
String getName();
/**
* 插件版本
*/
String getVersion();
/**
* 支持的验证类型
*/
List<String> getSupportedTypes();
/**
* 创建验证器
*/
FieldValidator createValidator(String type, Map<String, Object> config);
/**
* 插件初始化
*/
void initialize(ValidationContext context);
/**
* 插件销毁
*/
void destroy();
}
/**
* 插件管理器
*/
@Component
public class ValidationPluginManager {
private final Map<String, ValidationPlugin> plugins = new ConcurrentHashMap<>();
private final Map<String, FieldValidator> validators = new ConcurrentHashMap<>();
/**
* 注册插件
*/
public void registerPlugin(ValidationPlugin plugin) {
plugins.put(plugin.getName(), plugin);
// 注册插件支持的验证器
for (String type : plugin.getSupportedTypes()) {
validators.put(type, plugin.createValidator(type, Collections.emptyMap()));
}
log.info("Registered validation plugin: {} v{}", plugin.getName(), plugin.getVersion());
}
/**
* 获取验证器
*/
public FieldValidator getValidator(String type) {
return validators.get(type);
}
/**
* 动态加载插件
*/
public void loadPlugin(String pluginPath) {
try {
// 实现动态类加载逻辑
URLClassLoader classLoader = new URLClassLoader(new URL[]{new File(pluginPath).toURI().toURL()});
// 加载插件类并注册
} catch (Exception e) {
log.error("Failed to load validation plugin: {}", pluginPath, e);
}
}
}
2. 规则引擎集成
/**
* 规则引擎验证器
*/
@Component
public class RuleEngineValidator implements FieldValidator {
@Autowired
private DroolsRuleEngine ruleEngine;
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
try {
// 构建规则执行上下文
RuleContext context = new RuleContext();
context.setFieldName(rule.getFieldName());
context.setFieldValue(value);
context.setRuleExpression(rule.getRuleExpression());
// 执行规则
RuleResult result = ruleEngine.execute(context);
if (result.isValid()) {
return ValidationResult.success();
} else {
return ValidationResult.error(result.getErrorMessage());
}
} catch (Exception e) {
log.error("Rule engine validation failed", e);
return ValidationResult.error("规则执行失败: " + e.getMessage());
}
}
}
/**
* Drools规则引擎实现
*/
@Component
public class DroolsRuleEngine {
private KieContainer kieContainer;
@PostConstruct
public void initialize() {
KieServices kieServices = KieServices.Factory.get();
kieContainer = kieServices.getKieClasspathContainer();
}
public RuleResult execute(RuleContext context) {
KieSession kieSession = kieContainer.newKieSession();
try {
// 插入事实
kieSession.insert(context);
// 执行规则
kieSession.fireAllRules();
// 获取结果
return context.getResult();
} finally {
kieSession.dispose();
}
}
}
3. 机器学习验证
/**
* 机器学习验证器
*/
@Component
public class MLValidator implements FieldValidator {
@Autowired
private MLModelService mlModelService;
@Override
public ValidationResult validate(Object value, ValidationRule rule) {
try {
String modelName = rule.getCustomValidator();
if (StringUtils.isBlank(modelName)) {
return ValidationResult.success();
}
// 准备特征数据
Map<String, Object> features = prepareFeatures(value, rule);
// 调用ML模型进行预测
MLPredictionResult prediction = mlModelService.predict(modelName, features);
if (prediction.isValid()) {
return ValidationResult.success();
} else {
return ValidationResult.error("数据异常,置信度: " + prediction.getConfidence());
}
} catch (Exception e) {
log.error("ML validation failed", e);
return ValidationResult.success(); // ML验证失败时不阻止业务流程
}
}
private Map<String, Object> prepareFeatures(Object value, ValidationRule rule) {
Map<String, Object> features = new HashMap<>();
features.put("value", value);
features.put("fieldName", rule.getFieldName());
features.put("dataType", value != null ? value.getClass().getSimpleName() : "null");
// 添加更多特征...
return features;
}
}
4. 分布式验证
/**
* 分布式验证服务
*/
@Service
public class DistributedValidationService {
@Autowired
private ValidationClusterManager clusterManager;
@Autowired
private LoadBalancer loadBalancer;
/**
* 分布式表单验证
*/
public CompletableFuture<FormValidationResult> validateFormDistributed(FormValidationRequest request) {
// 将表单字段分组,分配到不同的验证节点
Map<String, List<String>> fieldGroups = groupFields(request.getFormData().keySet());
List<CompletableFuture<Map<String, FieldValidationResult>>> futures = new ArrayList<>();
for (Map.Entry<String, List<String>> entry : fieldGroups.entrySet()) {
String nodeId = entry.getKey();
List<String> fields = entry.getValue();
CompletableFuture<Map<String, FieldValidationResult>> future =
validateFieldsOnNode(nodeId, request, fields);
futures.add(future);
}
// 等待所有验证完成并合并结果
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> {
FormValidationResult result = new FormValidationResult();
Map<String, FieldValidationResult> allFieldResults = new HashMap<>();
for (CompletableFuture<Map<String, FieldValidationResult>> future : futures) {
try {
allFieldResults.putAll(future.get());
} catch (Exception e) {
log.error("Failed to get validation result", e);
}
}
result.setFieldResults(allFieldResults);
result.setValid(allFieldResults.values().stream().allMatch(FieldValidationResult::isValid));
return result;
});
}
private Map<String, List<String>> groupFields(Set<String> fields) {
// 实现字段分组逻辑,将字段分配到不同的验证节点
Map<String, List<String>> groups = new HashMap<>();
List<String> availableNodes = clusterManager.getAvailableNodes();
int nodeIndex = 0;
for (String field : fields) {
String nodeId = availableNodes.get(nodeIndex % availableNodes.size());
groups.computeIfAbsent(nodeId, k -> new ArrayList<>()).add(field);
nodeIndex++;
}
return groups;
}
private CompletableFuture<Map<String, FieldValidationResult>> validateFieldsOnNode(
String nodeId, FormValidationRequest request, List<String> fields) {
return CompletableFuture.supplyAsync(() -> {
try {
// 调用远程验证服务
ValidationServiceClient client = clusterManager.getClient(nodeId);
Map<String, Object> fieldData = new HashMap<>();
for (String field : fields) {
fieldData.put(field, request.getFormData().get(field));
}
PartialFormValidationRequest partialRequest = new PartialFormValidationRequest();
partialRequest.setUri(request.getUri());
partialRequest.setFieldData(fieldData);
partialRequest.setOperation(request.getOperation());
return client.validateFields(partialRequest);
} catch (Exception e) {
log.error("Failed to validate fields on node: {}", nodeId, e);
// 返回失败结果
Map<String, FieldValidationResult> errorResults = new HashMap<>();
for (String field : fields) {
FieldValidationResult errorResult = new FieldValidationResult();
errorResult.setValid(false);
errorResult.addError("验证服务不可用");
errorResults.put(field, errorResult);
}
return errorResults;
}
});
}
}
总结
数据验证设计思路的核心在于构建一个分层、可配置、可扩展的验证体系,确保数据质量和业务规则的合规性。
关键设计要点
- 分层验证架构
- 前端验证提升用户体验
- 接口验证确保数据安全
- 业务验证保证规则合规
- 数据库约束作为最后防线
- 可配置化管理
- 验证规则通过数据库配置
- 支持动态加载和更新
- 提供可视化配置界面
- 高性能设计
- 验证规则缓存机制
- 异步验证处理
- 批量验证优化
- 扩展性支持
- 插件化验证器架构
- 自定义验证器接口
- 规则引擎集成能力
- 用户友好性
- 实时验证反馈
- 清晰的错误信息
- 多语言支持
实施建议
- 分阶段实施
- 第一阶段:基础验证功能
- 第二阶段:业务规则验证
- 第三阶段:高级特性和优化
- 性能监控
- 建立验证性能指标
- 监控验证成功率
- 优化验证响应时间
- 持续改进
- 收集用户反馈
- 优化验证规则
- 扩展验证能力
通过这套完整的数据验证设计方案,可以显著提升系统的数据质量、用户体验和业务规则的执行效果,为CRUD表单系统提供强有力的数据保障。
文档版本: 1.0
创建时间:
适用项目: CRUD表单系统
维护人: 开发团队
8239

被折叠的 条评论
为什么被折叠?



