前言
參數校驗是我們程式開發中必不可少的過程。使用者在前端頁面上填寫表單時,前端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;
}
-
自定義注解
https://blog.csdn.net/panchang199266/article/details/83050053
- 使用
/**
* 開啟校驗注解:@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