一、背景
在我們編寫程式的過程中,程式中可能隨時發生各種異常,那麼我們如何優雅的處理各種異常呢?
二、需求
1、攔截系統中部分異常,返回自定義的響應。
比如:
系統發生HttpRequestMethodNotSupportedException
異常,我們需要返回如下資訊。
http的狀態碼:返回 405
{
code: 自定義異常碼,
message: 錯誤訊息
}
2、實現自定義異常的攔截
攔截我們自己寫的 BizException
三、編寫一些異常基礎程式碼
1、引入jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
注意:
引入spring-boot-starter-validation
是為了驗證請求的中的引數,然後當引數不滿足時丟擲異常。
2、定義一個自定義異常
public class BizException extends RuntimeException {
public BizException() {
}
public BizException(String message) {
super(message);
}
public BizException(String message, Throwable cause) {
super(message, cause);
}
public BizException(Throwable cause) {
super(cause);
}
public BizException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
3、編寫一個簡單的控制層
@RestController
@RequestMapping("exception")
public class ExceptionTestController {
static class Req {
@NotBlank
public String password;
}
@PostMapping("password")
public String checkPassword(@Validated @RequestBody Req req) {
if (Objects.equals(req.password, "exception")) {
throw new BizException("密碼傳遞的是exception字串");
}
return "當前密碼,password: " + req.password;
}
}
解釋
提供一個 /exception/password
api,需要傳遞一個password
引數
1、當不傳遞 password 引數時將丟擲MethodArgumentNotValidException異常。
2、當password傳遞exception引數時,則丟擲BizException異常。
4、測試
1、不傳遞password引數響應是什麼
1、使用預設的DefaultHandlerExceptionResolver處理
這個類DefaultHandlerExceptionResolver
是預設自動配置的。從上圖中可以看出有一個預設欄位的返回值
2、使用ResponseEntityExceptionHandler處理
1、編寫異常處理程式碼-使用預設的邏輯
@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// 此處自定義返回值
return super.handleMethodArgumentNotValid(ex, headers, status, request);
}
}
可以看到handleMethodArgumentNotValid
方法直接呼叫父類的方法,即使用預設的處理方式。
從上圖中可以看出返回值是空
2、編寫異常處理程式碼-返回值返回自定義內容
@Component
@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
// 此處自定義返回值
return super.handleMethodArgumentNotValid(ex, headers, status, request);
}
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
// 自定義請求返回值
Map<String, Object> body = new HashMap<>(4);
body.put("code", "錯誤碼");
body.put("message", "當前請求的方法不支援,支援的請求方法為:" + supportedMethods);
return new ResponseEntity<>(body, headers, status);
}
}
由上面的程式碼可知handleHttpRequestMethodNotSupported
方法返回了自定義的body。從上圖中可以看出,返回了我們自己定義的返回值。
2、password引數傳遞exception
1、使用ResponseEntityExceptionHandler或DefaultHandlerExceptionResolver處理
由上圖可知返回結果不對,我們需要自定義返回結果。
2、返回自定義異常
1、編寫BizException處理程式碼
@RestControllerAdvice
public class BizExceptionHandler {
@ExceptionHandler(BizException.class)
public ResponseEntity<Object> handleBizException(BizException exception) {
// 自定義請求返回值
Map<String, Object> body = new HashMap<>(4);
body.put("code", "錯誤碼");
body.put("message", "異常資訊為:" + exception.getMessage());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2、測試返回結果
從上圖可知返回了自定義資訊
四、注意事項
1、如果實現自定義異常處理
- 類上使用
@RestControllerAdvice
註解 - 方法上使用
@ExceptionHandler
來處理特定的異常
2、ResponseEntityExceptionHandler預設處理那些異常
3、使用了ResponseEntityExceptionHandler後,為什麼發生了異常後返回體為空
預設情況下,實現了 ResponseEntityExceptionHandler
這個類後,這個類處理的所有異常的響應結果都是 null
,如果想返回別的值需要我們自己去處理。
五、總結
1、如果我們想處理自定義異常,則可以使用 @RestControllerAdvice
|| @ControllerAdvice
配置@ExceptionHandler
來使用。
2、如果我們實現了ResponseEntityExceptionHandler
來處理異常,那麼預設的異常的響應結果為空,如果想不為空,則需要我們自己處理。
3、預設情況下,標準的Spring MVC異常會通過DefaultHandlerExceptionResolver
來處理。
六、程式碼實現
https://gitee.com/huan1993/spring-cloud-parent/tree/master/springboot/springboot-exception-handler