本文目录导读:

- 项目依赖配置
- 实体类 - User.java
- 控制器 - UserController.java
- 分组接口定义
- 修改实体类支持分组校验
- 全局异常处理 - GlobalExceptionHandler.java
- JSP页面 - register.jsp
- Spring配置文件
- 自定义校验注解示例
- RESTful API示例
- 测试用例
我将为您展示一个完整的Spring MVC数据校验案例,涵盖从依赖配置到控制器处理的完整流程。
项目依赖配置
Maven依赖 (pom.xml)
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.30</version>
</dependency>
<!-- Hibernate Validator (JSR-380实现) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.5.Final</version>
</dependency>
<!-- Java Bean Validation API -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- JSTL标签库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
实体类 - User.java
package com.example.entity;
import javax.validation.constraints.*;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
public class User {
@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字符之间")
private String password;
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄不能小于18岁")
@Max(value = 100, message = "年龄不能超过100岁")
private Integer age;
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@NotNull(message = "生日不能为空")
@Past(message = "生日必须是过去的时间")
private Date birthday;
@NotNull(message = "性别不能为空")
private String gender;
@Size(max = 200, message = "个人简介不能超过200个字符")
private String bio;
// 自定义校验方法 - 密码确认
@AssertTrue(message = "两次密码输入不一致")
public boolean isPasswordConfirmed() {
if (password == null || confirmPassword == null) {
return false;
}
return password.equals(confirmPassword);
}
// Getters and Setters
// ... 省略getter/setter方法
}
控制器 - UserController.java
package com.example.controller;
import com.example.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
// 显示注册页面
@GetMapping("/register")
public String showRegisterForm(Model model) {
model.addAttribute("user", new User());
return "register";
}
// 传统表单校验
@PostMapping("/register")
public String register(@Valid @ModelAttribute("user") User user,
BindingResult bindingResult,
Model model) {
// 如果有验证错误
if (bindingResult.hasErrors()) {
// 收集所有验证错误信息
Map<String, String> errors = new HashMap<>();
for (FieldError error : bindingResult.getFieldErrors()) {
errors.put(error.getField(), error.getDefaultMessage());
}
model.addAttribute("errors", errors);
model.addAttribute("user", user);
return "register";
}
// 校验通过,处理注册逻辑
model.addAttribute("message", "注册成功!");
return "success";
}
// 使用@Validated校验单个参数
@PostMapping("/checkUsername")
@ResponseBody
public Map<String, Object> checkUsername(@RequestParam @Pattern(regexp = "^[a-zA-Z0-9_]{3,20}$",
message = "用户名格式不正确") String username) {
Map<String, Object> result = new HashMap<>();
// 模拟检查用户名是否可用
boolean available = !username.equalsIgnoreCase("admin");
result.put("available", available);
result.put("message", available ? "用户名可用" : "用户名已被使用");
return result;
}
// 使用分组校验
@PostMapping("/update")
public String updateUser(@Validated(UpdateGroup.class) @ModelAttribute("user") User user,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "register";
}
return "success";
}
}
分组接口定义
package com.example.validation;
// 注册分组
public interface RegisterGroup {
}
// 更新分组
public interface UpdateGroup {
}
修改实体类支持分组校验
package com.example.entity;
import javax.validation.constraints.*;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import com.example.validation.RegisterGroup;
import com.example.validation.UpdateGroup;
public class User {
@NotBlank(message = "用户名不能为空", groups = RegisterGroup.class)
@Size(min = 3, max = 20, message = "用户名长度必须在3-20字符之间", groups = {RegisterGroup.class, UpdateGroup.class})
private String username;
@NotBlank(message = "密码不能为空", groups = RegisterGroup.class)
@Size(min = 6, max = 20, message = "密码长度必须在6-20字符之间", groups = RegisterGroup.class)
private String password;
@NotBlank(message = "邮箱不能为空", groups = RegisterGroup.class)
@Email(message = "邮箱格式不正确", groups = {RegisterGroup.class, UpdateGroup.class})
private String email;
@Min(value = 18, message = "年龄不能小于18岁", groups = {RegisterGroup.class, UpdateGroup.class})
@Max(value = 100, message = "年龄不能超过100岁", groups = {RegisterGroup.class, UpdateGroup.class})
private Integer age;
// 其他字段省略...
}
全局异常处理 - GlobalExceptionHandler.java
package com.example.exception;
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.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
Map<String, Object> result = new HashMap<>();
Map<String, String> errors = new HashMap<>();
for (FieldError error : ex.getBindingResult().getFieldErrors()) {
errors.put(error.getField(), error.getDefaultMessage());
}
result.put("code", 400);
result.put("message", "参数校验失败");
result.put("errors", errors);
return result;
}
// 处理参数校验异常(基于方法参数)
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, Object> handleConstraintViolation(ConstraintViolationException ex) {
Map<String, Object> result = new HashMap<>();
Map<String, String> errors = new HashMap<>();
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
errors.put(violation.getPropertyPath().toString(), violation.getMessage());
}
result.put("code", 400);
result.put("message", "参数校验失败");
result.put("errors", errors);
return result;
}
}
JSP页面 - register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>用户注册</title>
<style>
.error { color: red; font-size: 12px; }
.form-group { margin-bottom: 15px; }
label { display: inline-block; width: 100px; }
</style>
</head>
<body>
<h2>用户注册</h2>
<form:form modelAttribute="user" method="post" action="register">
<div class="form-group">
<label>用户名:</label>
<form:input path="username" />
<form:errors path="username" cssClass="error" />
<c:if test="${errors['username'] != null}">
<span class="error">${errors['username']}</span>
</c:if>
</div>
<div class="form-group">
<label>密码:</label>
<form:password path="password" />
<form:errors path="password" cssClass="error" />
</div>
<div class="form-group">
<label>确认密码:</label>
<form:password path="confirmPassword" />
<form:errors path="passwordConfirmed" cssClass="error" />
</div>
<div class="form-group">
<label>邮箱:</label>
<form:input path="email" />
<form:errors path="email" cssClass="error" />
</div>
<div class="form-group">
<label>年龄:</label>
<form:input path="age" />
<form:errors path="age" cssClass="error" />
</div>
<div class="form-group">
<label>手机号:</label>
<form:input path="phone" />
<form:errors path="phone" cssClass="error" />
</div>
<div class="form-group">
<label>生日:</label>
<form:input path="birthday" />
<form:errors path="birthday" cssClass="error" />
</div>
<div class="form-group">
<label>性别:</label>
<form:radiobutton path="gender" value="male" />男
<form:radiobutton path="gender" value="female" />女
<form:errors path="gender" cssClass="error" />
</div>
<div class="form-group">
<label>个人简介:</label>
<form:textarea path="bio" rows="3" cols="30" />
<form:errors path="bio" cssClass="error" />
</div>
<div class="form-group">
<input type="submit" value="注册" />
</div>
</form:form>
</body>
</html>
Spring配置文件
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 启用Spring MVC注解驱动 -->
<mvc:annotation-driven />
<!-- 扫描控制器 -->
<context:component-scan base-package="com.example.controller" />
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置本地化解析器和消息源 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:ValidationMessages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
</beans>
自定义校验注解示例
package com.example.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPhone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 校验器实现
package com.example.validation;
import com.example.annotation.ValidPhone;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {
private static final String PHONE_PATTERN = "^1[3-9]\\d{9}$";
@Override
public void initialize(ValidPhone constraintAnnotation) {
}
@Override
public boolean isValid(String phone, ConstraintValidatorContext context) {
if (phone == null || phone.isEmpty()) {
return true; // 是否为空由@NotBlank处理
}
return phone.matches(PHONE_PATTERN);
}
}
RESTful API示例
package com.example.controller;
import com.example.entity.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/users")
@Validated
public class UserApiController {
// 创建用户
@PostMapping
public Map<String, Object> createUser(@Valid @RequestBody User user) {
Map<String, Object> result = new HashMap<>();
// 处理创建逻辑
result.put("code", 200);
result.put("message", "用户创建成功");
result.put("data", user);
return result;
}
// 查询用户(带参数校验)
@GetMapping("/{id}")
public Map<String, Object> getUser(@PathVariable @Min(value = 1, message = "用户ID必须大于0") Long id) {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("data", "用户" + id + "的信息");
return result;
}
// 更新邮箱
@PutMapping("/{id}/email")
public Map<String, Object> updateEmail(@PathVariable Long id,
@RequestParam @Email(message = "邮箱格式不正确") String email) {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "邮箱更新成功");
return result;
}
}
测试用例
package com.example.controller;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testRegisterWithInvalidData() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/user/register")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "a") // 太短
.param("password", "123") // 太短
.param("email", "invalid-email") // 无效邮箱
.param("age", "15")) // 年龄太小
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("register"));
}
@Test
public void testRegisterWithValidData() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/user/register")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "john123")
.param("password", "password123")
.param("confirmPassword", "password123")
.param("email", "john@example.com")
.param("age", "25")
.param("phone", "13812345678")
.param("birthday", "1995-01-01")
.param("gender", "male"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("success"));
}
}
这个完整的Spring MVC数据校验案例展示了:
- 核心功能:表单验证、参数验证、分组验证
- 技术特点:使用JSR-380规范、自定义注解、全局异常处理
- 实现方式:注解驱动、验证器、分组校验
- UI展示:错误消息显示、错误样式定制
- API支持:RESTful API的参数校验
这个案例可以很好地集成到实际项目中,提供完善的数据校验机制。
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播