本文目录导读:

我来为你整理一个完善的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防护、防重复提交
- 用户体验:密码强度显示、实时验证
- 维护性:模块化设计、错误信息集中管理
建议根据实际需求选择合适的校验方式,前端主要提升用户体验,后端确保数据安全。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播