目录
- Javax Bean Validation
- 在Spring Boot中集成Javax Bean Validation
- 使用案例
- 功能测试
- 配置全局异常处理器
- 重新测试
- 返回特定形式的信息
- 方式一
- 方式二
- 附:常用的注解
Javax Bean Validation
Javax Bean Validation是Java平台的一项规范,旨在提供一种简单且可扩展的方式来验证Java对象的数据。它提供了一组注解,可以应用于Java Bean的属性上,以定义验证规则,并提供了一组验证器来执行这些规则。
在Spring Boot中集成Javax Bean Validation
本文基于SSM框架(可参考博客:【Java】使用IntelliJ IDEA搭建SSM(MyBatis-Plus)框架并连接MySQL数据库)
Spring Boot对Javax Bean Validation提供了内置支持。
在pom.xml文件中添加依赖(包含在标签dependencies
中):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>9.0.83</version>
</dependency>
这些依赖会自动包含Javax Bean Validation API以及Hibernate Validator作为实现。
其中spring-boot-starter-validation
的版本由其parent确定:
<!-- 定义父项目,使用Spring Boot 的版本管理 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
使用案例
以用户注册功能为例,验证用户输入的用户名和密码。
- 创建一个名为
User
的Java实体类:
import lombok.Data;
import javax.validation.constraints.Size;
import javax.validation.constraints.NotBlank;
@Data
public class User {
@NotBlank(message = "用户名不能为空")
private String username;
@Size(min = 6, message = "密码长度不能少于6位")
private String password;
}
在User类中,使用了@NotBlank
和@Size
注解来定义了对用户名和密码的验证规则。
- 创建
UserMapper
,UserMapperxml
,UserService
,UserServiceImpl
:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.z.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.z.mapper.UserMapper">
</mapper>
import com.baomidou.mybatisplus.extension.service.IService;
import com.z.entity.User;
public interface UserService extends IService<User> {
}
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.z.entity.User;
import com.z.mapper.UserMapper;
import com.z.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
- 创建一个REST控制器来处理用户注册请求:
import com.z.entity.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
public class UserController {
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody @Valid User user) {
// 处理用户注册逻辑
return ResponseEntity.ok("用户注册成功");
}
}
在UserController中,使用了@Valid
注解来告诉Spring Boot验证User对象,并通过@RequestBody
注解将请求体映射到User对象上。
- 新建一个数据库及与实体类对应的数据表,并配置数据库:
在resources下添加application.yml
文件:
server:
# 端口
port: 8080
spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yourdatabase?characterEncoding=utf-8
username: yourusername
password: yourpassword
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
# mapper文件映射路径
mapper-locations: classpath*:mapper/*.xml
configuration:
# 打印SQL语句
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
其中,yourusername
,yourpassword
,yourdatabase
注意换成自己的。
- 编写
Main.java
运行项目,并通过IDEA的启动按钮启动项目:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
功能测试
使用Postman发送以下JSON请求体:
点击send
,接收到一个400 Bad Request响应:
可以观察到,之前写在注解中的message并没有展示,需配置全局异常处理器处理异常。
配置全局异常处理器
创建一个@ControllerAdvice
类,并在其中定义一个方法来处理MethodArgumentNotValidException
异常:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
StringBuilder errors = new StringBuilder();
ex.getBindingResult().getFieldErrors().forEach(error -> errors.append(error.getDefaultMessage()).append("\n"));
return ResponseEntity.badRequest().body(errors.toString());
}
}
重新测试
重新运行程序测试,并发送请求,得到如下结果:
可以观察到,这里仅仅是将错误信息以文本形式展示,并没有返回状态码等信息。
返回特定形式的信息
方式一
重写全局异常处理器:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, Object> body = new HashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", 400);
body.put("error", "Bad Request");
StringBuilder errors = new StringBuilder();
ex.getBindingResult().getFieldErrors().forEach(error -> errors.append(error.getDefaultMessage()).append(System.lineSeparator()));
body.put("message", errors.toString());
return ResponseEntity.badRequest().body(body);
}
}
测试结果:
方式二
- 定义一个返回结果类型
ApiResult
:
import lombok.Data;
import java.util.List;
@Data
public class ApiResult {
// 定义状态码
public static final int OK = 200;
public static final int ERROR = 500;
public static final int Invalid = 400;
// 定义返回结果的字段
private int code;
private String message;
private Object data;
// 构造器
public ApiResult(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
// 静态方法创建成功的响应
public static ApiResult ok(String message, Object data) {
return new ApiResult(OK, message, data);
}
// 静态方法创建错误的响应
public static ApiResult error(String message) {
return new ApiResult(ERROR, message, null);
}
public static ApiResult violateConstraint(List<String> violation) {
return new ApiResult(Invalid, "参数校验未通过", violation);
}
}
- 修改全局异常处理器:
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 java.util.List;
import java.util.stream.Collectors;
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult handleValidationExceptions(MethodArgumentNotValidException ex) {
List<String> violations = ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).
collect(Collectors.toList());
return ApiResult.violateConstraint(violations);
}
}
- 修改
UserController
中注册接口的返回类型:
@RestController
@RequestMapping("/test")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public ApiResult registerUser(@RequestBody @Valid User user) {
userService.save(user);
return ApiResult.ok("用户注册成功",user);
}
}
测试结果:
附:常用的注解
注解 | 适用类型 | 说明 | 使用案例 |
---|---|---|---|
@Null | 任意类型 | 被注释的元素必须为null | @Null(message = "必须为null") |
@NotNull | 任意类型 | 被注释的元素不能为null | @NotNull(message = "不能为null") |
@NotEmpty | 字符串、集合、数组 | 被注释的元素不能为 null 也不能为空 | @NotEmpty(message = "不能为null或者为空") |
@NotBlank | 字符串 | 被注释的字符串不能为 null ,且去除空格后的长度不为 0 | @NotBlank(message = "name为必传参数") |
@Size | 字符串、集合、数组 | 被注解的元素的大小必须在指定的范围内 | @Size(min = 5,max = 20,message = "字符长度在 5 -20 之间") |
@Min | 数字 | 被注解的元素必须是一个数字,其值必须大于等于指定的最小值 | @Min(value = 0,message = "最小金额不能小于 0") |
@Max | 数字 | 被注解的元素必须是一个数字,其值必须小于等于指定的最大值 | @Max(value = 200,message = "最大金额不能超过 200") |
@DecimalMin | 数字 | 被注解的元素必须是一个数字,其值必须大于等于指定的最小值,可用于浮点数比较 | @DecimalMin(value = "0.1",message = "该参数不能小于 0.1") |
@DecimalMax | 数字 | 被注解的元素必须是一个数字,其值必须小于等于指定的最大值,可用于浮点数比较 | @DecimalMax(value = "100.4",message = "该参数不能大于 100.4") |
@Digits | 数字 | 被注解的元素必须是一个数字,且其值必须在指定的范围内 | @Digits(integer = 3,fraction = 2,message = "该参数整数位数不能超出3位,小数位数不能超过2位") |
@Negative | 数字 | 被注释的元素必须是负数 | @Negative(message = "必须是负数") |
@NegativeOrZero | 数字 | 被注释的元素必须是负数或 0 | @NegativeOrZero(message = "必须是负数或者为0") |
@Positive | 数字 | 被注释的元素必须是正数 | @Positive(message = "必须是正数") |
@PositiveOrZero | 数字 | 被注释的元素必须是正数或0 | @PositiveOrZero(message = "必须是正数或者为0") |
@Pattern | 字符串 | 被注解的字符串必须符合指定的正则表达式 | @Pattern(regexp = "^1[3456789]\d{9}$",message = "手机号格式不正确") |
@Email | 字符串 | 被注解的元素必须是一个电子邮件地址 | @Email(message = "email格式错误") |
@Future | 日期、时间 | 被注解的元素必须是一个将来的日期 | @Future(message = "预约日期要大于当前日期") |
@FutureOrPresent | 日期、时间 | 被注释的元素必须是现在或者未来的日期 | @FutureOrPresent(message = "预约日要大于当前日期") |
@AssertTrue | 布尔 | 被注解的元素必须是true | @AssertTrue(message = "该参数必须为 true") |
@AssertFalse | 布尔 | 被注解的元素必须是false | @AssertFalse(message = "该参数必须为 false") |