Java表单校验案例怎么完善

wen java案例 29

本文目录导读:

Java表单校验案例怎么完善

  1. 前端校验(JavaScript)
  2. 后端校验(Spring Boot)
  3. 前端异步提交(增强版)
  4. 完善建议

我来为你整理一个完善的Java表单校验案例,涵盖前后端校验的完整实现。

前端校验(JavaScript)

HTML表单结构

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">用户注册表单</title>
    <style>
        .error { color: red; font-size: 12px; }
        .success { border-color: green; }
        .error-input { border-color: red; }
    </style>
</head>
<body>
    <form id="registerForm" onsubmit="return validateForm()">
        <div>
            <label>用户名:</label>
            <input type="text" id="username" name="username" required>
            <span id="usernameError" class="error"></span>
        </div>
        <div>
            <label>密码:</label>
            <input type="password" id="password" name="password" required>
            <span id="passwordError" class="error"></span>
        </div>
        <div>
            <label>确认密码:</label>
            <input type="password" id="confirmPassword" name="confirmPassword" required>
            <span id="confirmPasswordError" class="error"></span>
        </div>
        <div>
            <label>邮箱:</label>
            <input type="email" id="email" name="email" required>
            <span id="emailError" class="error"></span>
        </div>
        <div>
            <label>手机号:</label>
            <input type="tel" id="phone" name="phone">
            <span id="phoneError" class="error"></span>
        </div>
        <div>
            <label>年龄:</label>
            <input type="number" id="age" name="age" min="1" max="120">
            <span id="ageError" class="error"></span>
        </div>
        <button type="submit">注册</button>
        <button type="reset">重置</button>
    </form>
    <script src="validator.js"></script>
</body>
</html>

JavaScript校验文件(validator.js)

class FormValidator {
    constructor(config) {
        this.config = {
            validClass: 'success',
            errorClass: 'error-input',
            onInputChange: true,
            ...config
        };
    }
    // 初始化校验
    init(formId) {
        const form = document.getElementById(formId);
        if (!form) return false;
        this.form = form;
        this.fields = form.querySelectorAll('input, select, textarea');
        // 实时校验
        if (this.config.onInputChange) {
            this.fields.forEach(field => {
                field.addEventListener('input', () => {
                    this.validateField(field);
                });
                field.addEventListener('blur', () => {
                    this.validateField(field);
                });
            });
        }
    }
    // 校验单个字段
    validateField(field) {
        const value = field.value.trim();
        const fieldName = field.name;
        let error = '';
        // 必填校验
        if (field.hasAttribute('required') && !value) {
            error = `${this.getFieldLabel(field)}不能为空`;
        }
        // 具体字段校验
        if (!error) {
            switch(fieldName) {
                case 'username':
                    error = this.validateUsername(value);
                    break;
                case 'password':
                    error = this.validatePassword(value);
                    if (!error) {
                        const confirmPassword = document.getElementById('confirmPassword');
                        if (confirmPassword && confirmPassword.value) {
                            this.validateField(confirmPassword);
                        }
                    }
                    break;
                case 'confirmPassword':
                    const password = document.getElementById('password').value;
                    error = this.validateConfirmPassword(value, password);
                    break;
                case 'email':
                    error = this.validateEmail(value);
                    break;
                case 'phone':
                    error = this.validatePhone(value);
                    break;
                case 'age':
                    error = this.validateAge(value);
                    break;
            }
        }
        // 显示错误信息
        this.showFieldError(field, error);
        return !error;
    }
    // 校验用户名
    validateUsername(username) {
        if (username.length < 3 || username.length > 20) {
            return '用户名长度应在3-20个字符之间';
        }
        if (!/^[a-zA-Z0-9_]+$/.test(username)) {
            return '用户名只能包含字母、数字和下划线';
        }
        return '';
    }
    // 校验密码
    validatePassword(password) {
        if (password.length < 6 || password.length > 20) {
            return '密码长度应在6-20个字符之间';
        }
        if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
            return '密码必须包含大小写字母和数字';
        }
        return '';
    }
    // 校验确认密码
    validateConfirmPassword(confirmPassword, password) {
        if (confirmPassword !== password) {
            return '两次输入的密码不一致';
        }
        return '';
    }
    // 校验邮箱
    validateEmail(email) {
        const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
        if (!emailRegex.test(email)) {
            return '请输入有效的邮箱地址';
        }
        return '';
    }
    // 校验手机号
    validatePhone(phone) {
        if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
            return '请输入有效的手机号码';
        }
        return '';
    }
    // 校验年龄
    validateAge(age) {
        if (age) {
            const ageNum = parseInt(age);
            if (isNaN(ageNum) || ageNum < 1 || ageNum > 120) {
                return '年龄必须在1-120之间';
            }
        }
        return '';
    }
    // 显示字段错误
    showFieldError(field, error) {
        const errorElement = document.getElementById(`${field.name}Error`);
        if (errorElement) {
            errorElement.textContent = error;
        }
        if (error) {
            field.classList.add(this.config.errorClass);
            field.classList.remove(this.config.validClass);
        } else {
            field.classList.remove(this.config.errorClass);
            if (field.value.trim()) {
                field.classList.add(this.config.validClass);
            }
        }
    }
    // 获取字段标签
    getFieldLabel(field) {
        const label = field.closest('div').querySelector('label');
        return label ? label.textContent.replace(':', '') : field.name;
    }
    // 校验整个表单
    validateForm() {
        let isValid = true;
        this.fields.forEach(field => {
            if (!this.validateField(field)) {
                isValid = false;
            }
        });
        return isValid;
    }
}
// 使用示例
const validator = new FormValidator({
    onInputChange: true
});
validator.init('registerForm');
// 表单提交
function validateForm() {
    return validator.validateForm();
}

后端校验(Spring Boot)

Maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

DTO对象(带校验注解)

import javax.validation.constraints.*;
import lombok.Data;
@Data
public class UserRegisterDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度应在3-20个字符之间")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
    private String username;
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度应在6-20个字符之间")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$", 
             message = "密码必须包含大小写字母和数字")
    private String password;
    @NotBlank(message = "确认密码不能为空")
    private String confirmPassword;
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
    @Min(value = 1, message = "年龄不能小于1")
    @Max(value = 120, message = "年龄不能大于120")
    private Integer age;
}

自定义校验注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchValidator.class)
@Documented
public @interface PasswordMatch {
    String message() default "两次输入的密码不一致";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

自定义校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, UserRegisterDTO> {
    @Override
    public boolean isValid(UserRegisterDTO dto, ConstraintValidatorContext context) {
        if (dto.getPassword() == null || dto.getConfirmPassword() == null) {
            return true; // 其他校验会处理null情况
        }
        return dto.getPassword().equals(dto.getConfirmPassword());
    }
}

Controller层

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/user")
@Validated
public class UserController {
    @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody UserRegisterDTO userDTO) {
        // 业务逻辑处理
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("message", "注册成功");
        return ResponseEntity.ok(result);
    }
    // 单个字段校验
    @PostMapping("/check-username")
    public ResponseEntity<?> checkUsername(
            @RequestParam @NotBlank(message = "用户名不能为空")
            @Size(min = 3, max = 20, message = "用户名长度不符合要求") String username) {
        // 检查用户名是否存在
        Map<String, Object> result = new HashMap<>();
        result.put("available", !"admin".equals(username));
        return ResponseEntity.ok(result);
    }
}

全局异常处理

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("errors", errors);
        return ResponseEntity.badRequest().body(result);
    }
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<?> handleConstraintViolation(ConstraintViolationException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getConstraintViolations().forEach(violation -> {
            String propertyPath = violation.getPropertyPath().toString();
            String message = violation.getMessage();
            errors.put(propertyPath, message);
        });
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("errors", errors);
        return ResponseEntity.badRequest().body(result);
    }
}

前端异步提交(增强版)

// 异步提交表单
async function submitFormAsync(event) {
    event.preventDefault();
    if (!validator.validateForm()) {
        return false;
    }
    const formData = new FormData(document.getElementById('registerForm'));
    const data = Object.fromEntries(formData.entries());
    try {
        const response = await fetch('/api/user/register', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data)
        });
        const result = await response.json();
        if (result.success) {
            alert('注册成功!');
            // 跳转或清空表单
        } else {
            // 显示服务端错误
            if (result.errors) {
                Object.keys(result.errors).forEach(field => {
                    const errorElement = document.getElementById(`${field}Error`);
                    if (errorElement) {
                        errorElement.textContent = result.errors[field];
                    }
                });
            }
        }
    } catch (error) {
        console.error('提交失败:', error);
        alert('提交失败,请稍后重试');
    }
}
// 绑定异步提交
document.getElementById('registerForm').onsubmit = submitFormAsync;

完善建议

安全增强

// XSS防护
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}
// 防重复提交
let isSubmitting = false;
async function preventDoubleSubmit(form) {
    if (isSubmitting) return;
    isSubmitting = true;
    try {
        // 提交逻辑
    } finally {
        isSubmitting = false;
    }
}

用户体验优化

// 实时校验显示强度
function checkPasswordStrength(password) {
    let strength = 0;
    if (password.length >= 8) strength++;
    if (password.match(/[a-z]+/)) strength++;
    if (password.match(/[A-Z]+/)) strength++;
    if (password.match(/[0-9]+/)) strength++;
    if (password.match(/[$@#&!]+/)) strength++;
    const levels = ['非常弱', '弱', '中等', '强', '非常强'];
    return levels[Math.min(strength, 4)];
}
// 密码强度UI
passwordInput.addEventListener('input', function() {
    const strength = checkPasswordStrength(this.value);
    const strengthBar = document.getElementById('passwordStrength');
    strengthBar.textContent = `密码强度: ${strength}`;
});

国际化支持

const messages = {
    'zh-CN': {
        required: '不能为空',
        username: '用户名',
        // ...
    },
    'en-US': {
        required: 'is required',
        username: 'Username',
        // ...
    }
};

这个完整的表单校验案例包含了:

  • 前端校验:实时反馈、字段级校验、自定义规则
  • 后端校验:注解校验、自定义校验器、全局异常处理
  • 安全措施:XSS防护、防重复提交
  • 用户体验:密码强度显示、实时验证
  • 维护性:模块化设计、错误信息集中管理

建议根据实际需求选择合适的校验方式,前端主要提升用户体验,后端确保数据安全。

抱歉,评论功能暂时关闭!