目录
- 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: 8080spring:# 数据源配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/yourdatabase?characterEncoding=utf-8username: yourusernamepassword: yourpasswordjackson:time-zone: GMT+8date-format: yyyy-MM-dd HH:mm:ssmybatis-plus:# mapper文件映射路径mapper-locations: classpath*:mapper/*.xmlconfiguration:# 打印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;@Datapublic 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 {@Autowiredprivate 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") |