参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。
那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。
校验框架介绍
bean validation
官网:http://beanvalidation.org
最新版本:http://beanvalidation.org/2.0/
Bean Validation 2.0 (JSR 380) 是java EE 8的一部分,但也可以用于以前的版本。
hibernate validation
官网:http://hibernate.org/validator/
官方文档:http://hibernate.org/validator/documentation/
jboss文档仓库:https://docs.jboss.org/hibernate/validator/
最新中文版:https://docs.jboss.org/hibernate/validator/4.3/reference/zh-CN/html_single
hibernate validation是bean validation目前最好的实现。
·强烈建议看官方文档,还是很容易理解的,看完中文再看英文。
兼容性
bean validation | hibernate validation | java | spring boot |
---|---|---|---|
1.1 | 5.4 series | 6+ | 1.5.*.RELEASE |
2.0 | 6.0 series | 8+ | 2.0.*.RELEASE |
Spring Boot支持
引入hibernate validation依赖, Spring Boot会自动配置Validator。
在SpringMVC的Controller中加校验约束立即生效;在jsonrpc接口上需在类上加@Validated注解,校验约束才会生效。
Spring Boot 1.5.*.RELEASE默认支持Bean Validation1.1,可强制使用Bean Validation2.0。
常用约束注解
看java api文档,都可以查看他们支持的数据类型,包路径javax.validation.constraints,共22个。
constraint | description |
---|---|
@Null | 必须为null |
@NotNull | 必须不为 null |
@AssertTrue | 必须为 true ,支持boolean、Boolean |
@AssertFalse | 必须为 false ,支持boolean、Boolean |
@Min(value) | 值必须小于value,支持BigDecimal、BigInteger,byte、shot、int、long及其包装类 |
@Max(value) | 值必须大于value,支持BigDecimal、BigInteger,byte、shot、int、long及其包装类 |
@DecimalMin(value) | 值必须小于value,支持BigDecimal、BigInteger、CharSequence,byte、shot、int、long及其包装类 |
@DecimalMax(value) | 值必须大于value,支持BigDecimal、BigInteger、CharSequence,byte、shot、int、long及其包装类 |
@Size(max=, min=) | 支持CharSequence、Collection、Map、Array |
@Digits (integer, fraction) | 必须是一个数字 |
@Negative | 必须是一个负数 |
@NegativeOrZero | 必须是一个负数或0 |
@Positive | 必须是一个正数 |
@PositiveOrZero | 必须是个正数或0 |
@Past | 必须是一个过去的日期 |
@PastOrPresent | 必须是一个过去的或当前的日期 |
@Future | 必须是一个将来的日期 |
@FutureOrPresent | 必须是一个未来的或当前的日期 |
@Pattern(regex=,flag=) | 必须符合指定的正则表达式 |
@NotBlank(message =) | 必须是一个非空字符串 |
必须是电子邮箱地址 | |
@NotEmpty | 被注释的字符串的必须非空 |
JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:
Hibernate validator 在JSR303的基础上对校验注解进行了扩展,扩展注解如下:
Spring validtor 同样扩展了jsr303,并实现了方法参数和返回值的校验
Spring 提供了MethodValidationPostProcessor类,用于对方法的校验
代码实现
添加JAR包依赖
在pom.xml中添加如下依赖:
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
最简单的参数校验
1、Model 中添加校验注解
public class Book {
private long id;
/**
* 书名
*/
@NotEmpty(message = "书名不能为空")
private String bookName;
/**
* ISBN号
*/
@NotNull(message = "ISBN号不能为空")
private String bookIsbn;
/**
* 单价
*/
@DecimalMin(value = "0.1",message = "单价最低为0.1")
private double price; // getter setter ....... }
2、在controller中使用此校验
/**
* 添加Book对象
* @param book
*/
@RequestMapping(value = "/book", method = RequestMethod.POST)
public void addBook(@RequestBody @Valid Book book) {
System.out.println(book.toString());
}
当访问这个post接口时,如果参数不符合Model中定义的话,程序中就回抛出400异常,并提示错误信息。
自定义校验注解
虽然jSR303和Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。
下面以“List数组中不能含有null元素”为实例自定义校验注解
1、注解定义如下:
package com.beiyan.validate.annotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义参数校验注解
* 校验 List 集合中是否有null 元素
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此处指定了注解的实现类为ListNotHasNullValidatorImpl
public @interface ListNotHasNull {
/**
* 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
*/
int value() default 0;
String message() default "List集合中不能含有null元素";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 定义List,为了让Bean的一个属性上可以添加多套规则
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
ListNotHasNull[] value();
}
}
2、注解实现类:
package com.beiyan.validate.annotation;
import org.springframework.stereotype.Service;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
/**
* 自定义注解ListNotHasNull 的实现类
* 用于判断List集合中是否含有null元素
*/
@Service
public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> {
private int value;
@Override
public void initialize(ListNotHasNull constraintAnnotation) {
//传入value 值,可以在校验中使用
this.value = constraintAnnotation.value();
}
public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
for (Object object : list) {
if (object == null) {
//如果List集合中含有Null元素,校验失败
return false;
}
}
return true;
}
}
3、model添加注解:
public class User {
//其他参数 .......
/**
* 所拥有的书籍列表
*/
@NotEmpty(message = "所拥有书籍不能为空")
@ListNotHasNull(message = "List 中不能含有null元素")
@Valid
private List<Book> books;
//getter setter 方法.......
}
使用方法同上,在在需要校验的Model上面加上@Valid 即可
分组验证
对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下
1、定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
*/
public interface PersonAddView {
}
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
*/
public interface PersonModifyView {
}
2、Model上添加注解时使用指明所述的分组
public class Person {
private long id;
/**
* 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
*/
@NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加、修改用户时名字不能为空", payload = ValidateErrorLevel.Info.class)
@ListNotHasNull.List({
@ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
@ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
private String name;
@NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
private String address;
@Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
@Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
private int age;
//getter setter 方法......
}
3、启用校验
此时启用校验和之前的不同,需要指明启用哪一组规则
/**
* 添加一个Person对象
* 此处启用PersonAddView 这个验证规则
* 备注:此处@Validated(PersonAddView.class) 表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,
* 若两个规则同时加上去,则只有第一套起作用
*/
@RequestMapping(value = "/person", method = RequestMethod.POST)
public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
System.out.println(person.toString());
}
/**
* 修改Person对象
* 此处启用PersonModifyView 这个验证规则
*/
@RequestMapping(value = "/person", method = RequestMethod.PUT)
public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {
System.out.println(person.toString());
}
Spring validator 方法级别的校验
JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,spring 在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验,实现如下:
1、实例化MethodValidationPostProcessor
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
2、在所要实现方法参数校验的类上面添加@Validated,如下
@RestController
@Validated
public class ValidateController {
}
3、在方法上面添加校验规则:
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String paramCheck(@Length(min = 10) @RequestParam String name) {
System.out.println(name);
return null;
}
当方法上面的参数校验失败,spring 框架就回抛出异常
{
"timestamp": 1476108200558,
"status": 500,
"error": "Internal Server Error",
"exception": "javax.validation.ConstraintViolationException",
"message": "No message available",
"path": "/test"
}
从此可以优雅的对参数进行校验了
写在后面的话:
本篇文章只列举了常用的几种校验方法,其实关于校验的内容还有很多:
校验信息的国际化显示,
组合参数校验,
message中使用EL表达式,
将校验信息绑定到ModelAndView等,这里就不一一列出了,下面这几篇文章写的也不错,读者可以参考:
将校验信息绑定到ModelAndView http://www.voidcn.com/blog/983836259/article/p-5794496.html
集成Bean Validation 1.1(JSR-349)到SpringMVC https://my.oschina.net/qjx1208/blog/200946
Spring Boot参数校验:https://www.cnblogs.com/cjsblog/p/8946768.html
SpringBoot全局异常捕获统一处理及整合Validation
全局异常捕获处理及参数校验
一、为什么要用全局异常处理?
在日常开发中,为了不抛出异常堆栈信息给前端页面,每次编写Controller层代码都要尽可能的catch住所有service层、dao层等异常,代码耦合性较高,且不美观,不利于后期维护。为解决该问题,计划将Controller层异常信息统一封装处理,且能区分对待Controller层方法返回给前端的String、Map、JSONObject、ModelAndView等结果类型。
二、应用场景是什么?
- 非常方便的去掉了try catch这类冗杂难看的代码,有利于代码的整洁和优雅
- 自定义参数校验时候全局异常处理会捕获异常,将该异常统一返回给前端,省略很多if else代码
- 当后端出现异常时,需要返回给前端一个友好的界面的时候就需要全局异常处理
- 因为异常时层层向上抛出的,为了避免控制台打印一长串异常信息
三、如何进行全局异常捕获和处理?
一共有两种方法:
- Spring的AOP(较复杂)
- @ControllerAdvice结合@ExceptionHandler(简单)
四、@ControllerAdvice和@ExceptionHandler怎么用?
- 1、Controller Advice字面上意思是“控制器通知”,Advice除了“劝告”、“意见”之外,还有“通知”的意思。可以将@ExceptionHandler(标识异常类型对应的处理方法)标记的方法提取出来,放到一个类里,并将加上@ControllerAdvice,这样,所有的控制器都可以用了
@ControllerAdvice
public class ControllerHandlers(){
@ExceptionHandler
public String errorHandler(Exception e){
return "error";
}
}
- 2、 因为@ControllerAdvice被@Componen标记,所以他可以被组件扫描到并放入Spring容器
- 3、 如果只想对一部分控制器通知,比如某个包下边的控制器,就可以这样写:
@ControllerAdvice("com.labor")
public class ControllerHandlers(){}
也可以直接写类名
@ControllerAdvice(basePackageClasses = ***.class)
public class ControllerHandlers(){}
也可以传多个类
@ControllerAdvice(assignableTypes = {***.class,***.class})
public class ControllerHandlers(){}
- 4、 控制器通知还有一个兄弟,@RestControllerAdvice,如果用了它,错误处理方法的返回值不会表示用的哪个视图,而是会作为HTTP body处理,即相当于错误处理方法加了@ResponseBody注解。
@RestControllerAdvice
public class ControllerHandlers(){
@ExceptionHandler
public String errorHandler(Exception e){
return "error";
}
}
- 5、 @ExceptionHandler注解的方法只能返回一种类型,在前后端分离开发中我们通常返回,统一返回类型和优化错误的提示,我们可以封装我们自己的返回Result
自定义基础接口类
首先定义一个基础的接口类,自定义的错误描述枚举类需实现该接口。
代码如下:
public interface BaseErrorInfoInterface {
/**
* 错误码
*/
Integer getCode();
/**
* 错误描述
*/
String getMessage();
}
自定义枚举类
然后我们这里在自定义一个枚举类,并实现该接口。
代码如下:
public enum CommonEnum implements BaseErrorInfoInterface {
// 数据操作错误定义
SUCCESS(200, "成功!"),
BODY_NOT_MATCH(400,"请求的数据格式不符!"),
SIGNATURE_NOT_MATCH(401,"请求的数字签名不匹配!"),
NOT_FOUND(404, "未找到该资源!"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
SERVER_BUSY(503,"服务器正忙,请稍后再试!"),
;
/**
* 错误码
*/
private Integer code;
/**
* 错误描述
*/
private String message;
CommonEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
自定义异常类
然后我们在来自定义一个异常类,用于处理我们发生的业务异常。
代码如下:
public class BizException extends RuntimeException {
/**
* 错误码
*/
protected Integer errorCode;
/**
* 错误信息
*/
protected String errorMsg;
public BizException() {
super();
}
public BizException(BaseErrorInfoInterface errorInfoInterface) {
super(errorInfoInterface.getMessage());
this.errorCode = errorInfoInterface.getCode();
this.errorMsg = errorInfoInterface.getMessage();
}
public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
super(errorInfoInterface.getMessage(), cause);
this.errorCode = errorInfoInterface.getCode();
this.errorMsg = errorInfoInterface.getMessage();
}
public BizException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public BizException(Integer errorCode, String errorMsg, Throwable cause) {
super(errorMsg, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getMessage() {
return errorMsg;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
自定义数据格式
顺便这里我们定义一下数据的传输格式。
代码如下:
public class Result<T> implements BaseErrorInfoInterface {
//返回码
private Integer code;
//提示信息
private String message;
//返回具体内容
private T data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
分页返回数据格式
public class Page<T> implements Serializable {
/**
* 每页最大数据条数
*/
private static final int MAX_PAGE_SIZE = 200;
/**
* 当前页
*/
private Integer pageNum;
/**
* 每页数量
*/
private Integer pageSize;
/**
* 总数据条数
*/
private Integer totalCount;
/**
* 页码总数
*/
private Integer totalPages;
/**
* 数据集合
*/
private Collection<T> items;
public static int getMaxPageSize() {
return MAX_PAGE_SIZE;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotalCount() {
return totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public Integer getTotalPages() {
return totalPages;
}
public void setTotalPages(Integer totalPages) {
this.totalPages = totalPages;
}
public Collection<T> getItems() {
return items;
}
public void setItems(Collection<T> items) {
this.items = items;
}
}
定义分页返回实体
public class PageResult<T> implements BaseErrorInfoInterface {
//返回码
private Integer code;
//提示信息
private String message;
//返回具体内容
private Page<T> content;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Page<T> getContent() {
return content;
}
public void setContent(Page<T> content) {
this.content = content;
}
}
定义结果处理Util
为了防止多次出现new Result()的代码造成冗余,增加一个封装统一返回格式工具类ResultUtil
public class ResultUtil {
/**
* 成功
*
* @return
*/
public static Result success() {
return success(null);
}
/**
* 成功
* @param data
* @return
*/
public static Result success(Object data) {
Result result = new Result();
result.setCode(CommonEnum.SUCCESS.getCode());
result.setMessage(CommonEnum.SUCCESS.getMessage());
result.setData(data);
return result;
}
/**
* 失败
*/
public static Result fail(BaseErrorInfoInterface baseErrorInfoInterface) {
Result result = new Result();
result.setCode(baseErrorInfoInterface.getCode());
result.setMessage(baseErrorInfoInterface.getMessage());
result.setData(null);
return result;
}
/**
* 失败
*/
public static Result fail(Integer code, String message) {
Result result = new Result();
result.setCode(code);
result.setMessage(message);
result.setData(null);
return result;
}
/**
* 失败
*/
public static Result fail( String message) {
Result result = new Result();
result.setCode(-1);
result.setMessage(message);
result.setData(null);
return result;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
public class PageResultUtil {
/**
* 成功
*
* @return
*/
public static PageResult success() {
return success(null);
}
/**
* 成功
* @param content
* @return
*/
public static PageResult success(Page content) {
PageResult pageResult = new PageResult();
pageResult.setCode(CommonEnum.SUCCESS.getCode());
pageResult.setMessage(CommonEnum.SUCCESS.getMessage());
pageResult.setContent(content);
return pageResult;
}
/**
* 失败
*/
public static PageResult fail(BaseErrorInfoInterface baseErrorInfoInterface) {
PageResult pageResult = new PageResult();
pageResult.setCode(baseErrorInfoInterface.getCode());
pageResult.setMessage(baseErrorInfoInterface.getMessage());
pageResult.setContent(null);
return pageResult;
}
/**
* 失败
*/
public static PageResult fail(Integer code, String message) {
PageResult pageResult = new PageResult();
pageResult.setCode(code);
pageResult.setMessage(message);
pageResult.setContent(null);
return pageResult;
}
/**
* 失败
*/
public static PageResult fail( String message) {
PageResult pageResult = new PageResult();
pageResult.setCode(-1);
pageResult.setMessage(message);
pageResult.setContent(null);
return pageResult;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
- 6、 完善全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 请求方式不支持
*/
@ExceptionHandler({HttpRequestMethodNotSupportedException.class})
public Result handleException(HttpRequestMethodNotSupportedException e) {
log.error(e.getMessage(), e);
return ResultUtil.fail("不支持' " + e.getMethod() + "'请求");
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public Result notFount(RuntimeException e) {
log.error("运行时异常:", e);
return ResultUtil.fail("运行时异常:" + e.getMessage());
}
/**
* 校验方法参数异常处理
* 捕获 MethodArgumentNotValidException 异常
* @param exception
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public Result MethodArgumentNotValidExceptionHandle(MethodArgumentNotValidException exception) {
List<FieldError> errors = exception.getBindingResult().getFieldErrors();
String errorMsg = errors.stream().map(e -> e.getField() + ":" + e.getDefaultMessage())
.reduce("", (s1, s2) -> s1 + s2);
return ResultUtil.fail(errorMsg);
}
/**
* 校验异常
*/
@ExceptionHandler(value = BindException.class)
public Result validationExceptionHandler(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String errorMesssage = "";
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorMesssage += fieldError.getDefaultMessage() + "!";
}
return ResultUtil.fail(errorMesssage);
}
/**
* 校验异常
*/
@ExceptionHandler(value = ConstraintViolationException.class)
public Result ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
List<String> msgList = new ArrayList<>();
while (iterator.hasNext()) {
ConstraintViolation<?> cvl = iterator.next();
msgList.add(cvl.getMessageTemplate());
}
return ResultUtil.fail(String.join(",",msgList));
}
/**
* 业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result businessException(BusinessException e) {
log.error(e.getErrorMsg(), e);
return ResultUtil.fail(e.getErrorMsg());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
log.error(e.getMessage(), e);
return ResultUtil.fail("服务器错误,请联系管理员");
}
}
六、@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了
- pom文件引入validation的jar包
<!-- 校验-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 等待校验的object
public class Person {
/**
* @PersonName(prefix = "song"):自定义注解
*/
@NotNull
@PersonName(prefix = "song")
private String name;
@Min(value = 18)
@Max(value = 30, message = "超过30岁的不要!")
private Integer age;
}
/**
* 开启校验注解:@Valid
*/
@RestController
public class PersonController {
@PostMapping("/person")
public Result<Person> savePerson(@Valid @RequestBody Person person){
return ResultUtil.success(person);
}
}
- 全局异常处理里有相应的处理方法
/**
* 校验异常
*/
@ExceptionHandler(value = BindException.class)
public Result<String> validationExceptionHandler(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String errorMesssage = "";
for (FieldError fieldError : bindingResult.getFieldErrors()) {
errorMesssage += fieldError.getDefaultMessage() + "!";
}
return ResultUtil.fail(errorMesssage);
}
被@RequestBody和@RequestParam注解的请求实体,校验异常类是不同的
七、自定义异常以及事务回滚
public class MyException extends RuntimeException {
//这个地方不要写exception,因为Spring是只对运行时异常进行事务回滚,
//如果抛出的是exception是不会进行事务回滚的。
}
如果是在service层里捕获异常统一去处理,那为了保证事务的回滚,需要抛出RuntimeException
try {
} catch (Exception e) {
e.printStackTrace();
logger.error("发生异常");
throw new RuntimeException();
}
参考:
https://www.cnblogs.com/beiyan/p/5946345.html
https://blog.csdn.net/songguopeng/article/details/98961787