表单校验是保障数据质量的重要环节,需要在前端和后端同时进行校验。本文详细介绍 IMS 系统的表单校验架构与实现。
校验规则定义
系统支持多种校验规则类型:
/* 校验规则类型 */
type ValidationType =
| 'required' // 必填
| 'email' // 邮箱格式
| 'phone' // 手机号
| 'url' // URL格式
| 'number' // 数字
| 'integer' // 整数
| 'min' // 最小值
| 'max' // 最大值
| 'minLength' // 最小长度
| 'maxLength' // 最大长度
| 'pattern' // 正则表达式
| 'range' // 范围
| 'enum' // 枚举值
| 'custom' // 自定义校验;
/* 校验规则定义 */
interface ValidationRule {
type: ValidationType;
message: string;
value?: any;
trigger?: 'blur' | 'change' | 'focus';
validator?: function;
}
/* 字段配置 */
interface FieldConfig {
key: string;
label: string;
rules: ValidationRule[];
}
前端校验实现
前端使用异步校验,提供即时反馈:
/* 前端校验器 */
class FrontendValidator {
validateField(value: any, rules: ValidationRule[]): Promise<string[]> {
return new Promise(async (resolve) => {
const errors: string[] = [];
for (const rule of rules) {
const error = await this.validateRule(value, rule);
if (error) {
errors.push(error);
}
}
resolve(errors);
});
}
async validateRule(value: any, rule: ValidationRule): Promise<string | null> {
/* 必填校验 */
if (rule.type === 'required') {
if (value === null || value === undefined || value === '') {
return rule.message;
}
}
/* 邮箱校验 */
if (rule.type === 'email' && value) {
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailPattern.test(value)) {
return rule.message;
}
}
/* 手机号校验 */
if (rule.type === 'phone' && value) {
const phonePattern = /^1[3-9]\d{9}$/;
if (!phonePattern.test(value)) {
return rule.message;
}
}
/* 最小值校验 */
if (rule.type === 'min' && value !== '') {
if (Number(value) < rule.value) {
return rule.message;
}
}
/* 长度校验 */
if (rule.type === 'minLength' && value) {
if (value.length < rule.value) {
return rule.message;
}
}
/* 自定义校验 */
if (rule.type === 'custom' && rule.validator) {
const valid = await rule.validator(value);
if (!valid) {
return rule.message;
}
}
return null;
}
}
后端校验实现
后端校验确保数据安全,支持服务端独立校验:
/* 后端校验器 */
class BackendValidator {
validate(data: Record<string, any>, schema: Record<string, ValidationRule[]>): void {
const errors: Record<string, string[]> = {};
for (const [field, rules] of Object.entries(schema)) {
const value = data[field];
const fieldErrors: string[] = [];
for (const rule of rules) {
const error = this.validateRule(value, rule);
if (error) {
fieldErrors.push(error);
}
}
if (fieldErrors.length > 0) {
errors[field] = fieldErrors;
}
}
/* 抛出校验错误 */
if (Object.keys(errors).length > 0) {
throw new ValidationException(errors);
}
}
validateRule(value: any, rule: ValidationRule): string | null {
/* 必填校验(严格模式) */
if (rule.type === 'required') {
if (value === null || value === undefined) {
return rule.message;
}
if (typeof value === 'string' && value.trim() === '') {
return rule.message;
}
}
/* 跳过空值的其他校验 */
if (value === null || value === undefined || value === '') {
return null;
}
/* 正则校验 */
if (rule.type === 'pattern' && rule.value) {
if (!new RegExp(rule.value).test(value)) {
return rule.message;
}
}
/* 枚举校验 */
if (rule.type === 'enum' && rule.value) {
if (!rule.value.includes(value)) {
return rule.message;
}
}
return null;
}
}
异步校验
支持服务端异步校验,如用户名唯一性检查:
/* 异步校验服务 */
class AsyncValidator {
constructor(private api: ApiService) {}
/* 用户名唯一性校验 */
async checkUsernameUnique(username: string, excludeId?: string): Promise<boolean> {
const params = { username };
if (excludeId) {
params['excludeId'] = excludeId;
}
const response = await this.api.get('/api/check/username', params);
return response.data.unique;
}
/* 邮箱唯一性校验 */
async checkEmailUnique(email: string, excludeId?: string): Promise<boolean> {
const params = { email };
if (excludeId) {
params['excludeId'] = excludeId;
}
const response = await this.api.get('/api/check/email', params);
return response.data.unique;
}
/* 异步校验整合到前端校验器 */
async validateAsync(value: any, validators: Function[]): Promise<string[]> {
const errors: string[] = [];
for (const validator of validators) {
const result = await validator(value);
if (result !== true) {
errors.push(result);
}
}
return errors;
}
}
校验提示优化
提供友好的错误提示体验:
/* 错误消息管理器 */
class ErrorMessageManager {
private messages = {
required: '此字段为必填项',
email: '请输入有效的邮箱地址',
phone: '请输入有效的手机号码',
number: '请输入有效的数字',
min: (min) => `数值不能小于 ${min}`,
max: (max) => `数值不能大于 ${max}`,
minLength: (len) => `至少需要输入 ${len} 个字符`,
maxLength: (len) => `最多只能输入 ${len} 个字符`,
pattern: '格式不正确',
unique: '该值已存在,请更换'
};
getMessage(rule: ValidationRule): string {
/* 优先使用自定义消息 */
if (rule.message) {
return rule.message;
}
/* 使用默认消息模板 */
const template = this.messages[rule.type];
if (typeof template === 'function') {
return template(rule.value);
}
return template || '校验失败';
}
}
校验设计原则
- 前端校验重在即时反馈,提升用户体验
- 后端校验是最后防线,不可或缺
- 异步校验注意防抖,避免频繁请求
- 统一错误提示风格,保持一致性
总结
- 前后端双重校验,保障数据安全
- 支持多种内置校验规则类型
- 自定义校验灵活扩展
- 异步校验用于唯一性检查