一、全局處理異常
SpringBoot中有一個 @RestControllerAdvice 的注解,使用該注解表示開啟了全局異常的捕獲,我們只需在自定義一個方法使用 @ExceptionHandler
注解然后定義捕獲異常的類型即可對這些捕獲的異常進行統一的處理。
import com.macro.mall.common.api.CommonResult; import com.macro.mall.common.exception.BusinessException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class AdviceController {
// 響應碼的定義 @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(Exception ex) { if (ex instanceof BusinessException) { return CommonResult.failed("請求有誤, 請稍后再試! " + ex.getMessage()); } return CommonResult.failed(); } }
二、優雅的參數校驗
spring-boot-starter-web 包里面有 hibernate-validator 包,不需要引用hibernate validator依賴。在 pom.xml 中添加上 spring-boot-starter-web 的依賴即可。
官網: http://hibernate.org/validator/documentation/
JSR 303 是Bean驗證的規范 ,Hibernate Validator 是該規范的參考實現,它除了實現規范要求的注解外,還額外實現了一些注解。
@Null | 被注釋的元素必須為 null |
@NotNull | 限制必須不為null |
@NotEmpty | 驗證注解的元素值不為 null 且不為空(字符串長度不為0、集合大小不為0) |
@NotBlank | 驗證注解的元素值不為空(不為null、去除首位空格后長度為0),不同於@NotEmpty,@NotBlank只應用於字符串且在比較時會去除字符串的空格 |
@Pattern(value) | 限制必須符合指定的正則表達式 |
@Size(max,min) | 限制字符長度必須在 min 到 max 之間(也可以用在集合上) |
驗證注解的元素值是Email,也可以通過正則表達式和flag指定自定義的email格式 | |
@Max(value) | 限制必須為一個不大於指定值的數字 |
@Min(value) | 限制必須為一個不小於指定值的數字 |
@DecimalMax(value) | 限制必須為一個不大於指定值的數字 |
@DecimalMin(value) | 限制必須為一個不小於指定值的數字 |
@AssertFalse | 限制必須為false (很少用) |
@AssertTrue | 限制必須為true (很少用) |
@Past | 限制必須是一個過去的日期 |
@Future | 限制必須是一個將來的日期 |
@Digits(integer,fraction) | 限制必須為一個小數,且整數部分的位數不能超過 integer,小數部分的位數不能超過 fraction (很少用) |
@Length | 被注釋的字符串的大小必須在指定的范圍內 |
@Size | 被注釋的list、map在指定的大小范圍 |
@Range | 被注釋的元素必須在合適的范圍內 |
@URL | 被注釋的元素必須是有效URL |
@Negative | 負數 |
@NegativeOrZero | 0或者負數 |
@Positive | 整數 |
@PositiveOrZero | 0或者整數 |
@Valid | 遞歸的對關聯的對象進行校驗(對象內部包含另一個對象作為屬性,屬性上加@Valid,可以驗證作為屬性的對象內部的驗證) |
例子:
(1)定義接口的入參類
@Data public class RegisterParam { /** * 手機號 */ @NotBlank(message = "手機號碼不允許為空") @Length(max = 11, min = 1, message = "手機號碼必須是11個字符之間") @Pattern(regexp = "^1[3|4|5|8][0-9]\\d{8}$", message = "電話號碼格式不正確") private String phone; /** * 驗證碼 */ @NotBlank(message = "動態校驗碼不允許為空") @Length(max = 6, min = 6, message = "動態校驗碼必須是6個字符之間") private String otpCode; @NotBlank(message = "用戶名不允許為空") @Length(max = 20, min = 4, message = "用戶名長度必須在4-20字符之間") private String username; @NotBlank(message = "密碼不允許為空") @Length(max = 20, min = 8, message = "密碼長度必須在8-20字符之間") private String password; }
(2)Controller中的接口定義,增加 @Valid 注解
@PostMapping("/register") public CommonResult register(@Valid @RequestBody Register register) throws BusinessException { int result = memberService.register(register); if (result > 0) { return CommonResult.success(null); } return CommonResult.failed(); }
(3)全局異常處理,處理 MethodArgumentNotValidException 的異常;
@RestControllerAdvice public class AdviceController { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(Exception ex) { if (ex instanceof BusinessException) { return CommonResult.failed("請求有誤, 請稍后再試: " + ex.getMessage()); } else if (ex instanceof MethodArgumentNotValidException) { MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex; List<ObjectError> allErrors = exception.getBindingResult().getAllErrors(); StringBuilder msgBuilder = new StringBuilder(); for (ObjectError allError : allErrors) { msgBuilder.append(allError.getDefaultMessage()).append("; "); } return CommonResult.failed("參數校驗錯誤:" + msgBuilder.toString()); } return CommonResult.failed(); } }
三、參數分組校驗
在實際開發中經常會遇到這種情況:想要用一個實體類去接收多個controller的參數,但是不同controller所需要的參數又有些許不同,而你又不想為這點不同去建個新的類接收參數。
比如新增用戶信息的時候,不需要驗證userId(因為系統生成),修改的時候需要驗證userId;這種時候就可以使用參數分組來實現。
(1)定義兩個空的接口: GroupA 和 GroupB
interface GroupA { } interface GroupB { }
(2)定義接口的入參類,增加 groups 屬性;
@Data public class Register { @NotBlank(message = "手機號碼不允許為空") @Length(max = 11, min = 1, message = "手機號碼必須是11個字符之間") @Pattern(regexp = "^1[3|4|5|8][0-9]\\d{8}$", message = "電話號碼格式不正確") private String phone; @NotBlank(message = "動態校驗碼不允許為空") @Length(max = 6, min = 6, message = "動態校驗碼必須是6個字符之間") private String otpCode; @NotBlank(message = "用戶名不允許為空", groups = GroupA.class) @Length(max = 20, min = 4, message = "用戶名長度必須在4-20字符之間") private String username; @NotBlank(message = "密碼不允許為空") @Length(max = 20, min = 8, message = "密碼長度必須在8-20字符之間") private String password; }
(3)controller 中的接口定義
@PostMapping("/register") public CommonResult register(@Validated({GroupA.class}) @RequestBody Register register) throws BusinessException { int result = memberService.register(register); if (result > 0) { return CommonResult.success(null); } return CommonResult.failed(); }
結果:只會校驗用戶名不允許為空;
參考:
(1)SpringBoot 參數校驗的方法: https://www.cnblogs.com/mooba/p/11276062.html
(2)@Validated和@Valid區別:https://blog.csdn.net/qq_27680317/article/details/79970590
(3)@Valid和@Validated的總結區分:https://www.jianshu.com/p/1dff31a1649d