在spring boot / cloud (二) 規範響應格式以及統一異常處理這篇博客中已經提到了使用@ExceptionHandler來處理各類類型的異常,這種方式也是互聯網上普遍的方式html
今天這篇博客,將介紹一種spring boot官方文檔上的統一處理異常的方式.你們能夠在spring boot 官方文檔查看介紹java
在開始介紹新的方法以前 , 咱們先來分析一下 , 之前的作法有那些地方是須要優化的git
一般咱們須要作統一異常處理的需求,大概都是要規範異常輸出,以及處理,通同一套抽象出來的邏輯來處理全部異常.spring
可是在當前流行RestFul風格接口的環境下,對異常的輸出還作了額外的一個要求,就是針對不一樣的錯誤須要輸出對應的http狀態.app
在前面的實現中,咱們大能夠指定一個處理Exception的@ExceptionHandler,這樣全部異常都能囊括了,可是卻沒法很好的將http狀態區分開來.框架
若是要實現不一樣的異常輸出不一樣的http狀態,在原來的作法裏就要將每一個異常都窮舉出來,而後作不一樣的設定.ide
顯然,咱們是不但願這樣作的,顯得太不聰明,不過還好,spring已經幫咱們把這一步已經作掉了,咱們只需處理本身關心的異常便可spring-boot
@ExceptionHandler(value = 要攔截的異常.class) @ResponseStatus(響應狀態) @ResponseBody public RestResponse<String> exception(要攔截的異常 exception) { return new RestResponse<>(ErrorCode.ERROR, buildError(exception)); } @ExceptionHandler(value = Exception.class) @ResponseStatus(500) @ResponseBody public RestResponse<String> exception(Exception exception) { return new RestResponse<>(ErrorCode.ERROR, buildError(exception)); }
在官方文檔中指出,你須要實現一個類,使用@ControllerAdvice標註,而後繼承至ResponseEntityExceptionHandler類.優化
這個ResponseEntityExceptionHandler類是一個抽象類,以下是它的核心方法ui
@ExceptionHandler({ NoSuchRequestHandlingMethodException.class, HttpRequestMethodNotSupportedException.class, .....省略 }) public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) { HttpHeaders headers = new HttpHeaders(); if (ex instanceof NoSuchRequestHandlingMethodException) { HttpStatus status = HttpStatus.NOT_FOUND; return handleNoSuchRequestHandlingMethod( (NoSuchRequestHandlingMethodException) ex, headers, status, request); } else if (ex instanceof HttpRequestMethodNotSupportedException) { HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED; return handleHttpRequestMethodNotSupported( (HttpRequestMethodNotSupportedException) ex, headers, status, request); } else if (..............){ .....省略 } else { .....省略 return handleExceptionInternal(ex, null, headers, status, request); } }
在以上的代碼片斷中,咱們能夠看到handleException方法已經把常見的異常都攔截掉了,而且作出了適當的處理,而且在最後,else分支裏,調用了handleExceptionInternal方法,
這個方法就是處理沒有被攔截到的異常,而後這也是咱們要進行擴展的地方
實現ExceptionHandle類,繼承至ResponseEntityExceptionHandler,而且註解@ControllerAdvice
@ControllerAdvice @Slf4j public class ExceptionHandle extends ResponseEntityExceptionHandler { ..... }
實現exception方法,使用@ExceptionHandler攔截Exception,那麼在這裏,全部的異常都會進入這個方法進行處理.
而後調用父類的handleException方法(上面提到的),讓spring默認的異常處理先處理一遍,若是當前的異常恰巧是被spring攔截的,那麼就用spring的默認實現處理,就無需在寫額外的代碼了,http狀態碼也一併的會設置好.
最後在調用咱們即將要重寫的方法handleExceptionInternal,來處理自定義異常以及規範異常輸出
@ExceptionHandler(value = Exception.class) public ResponseEntity<Object> exception(Exception ex, WebRequest request) { ResponseEntity<Object> objectResponseEntity = this.handleException(ex, request); return this.handleExceptionInternal(ex, null, objectResponseEntity.getHeaders(), objectResponseEntity.getStatusCode(), request); }
重寫handleExceptionInternal方法,
在這個方法裏面,能夠向以下實現同樣,去處理項目中自定義的異常,將其規範爲想要的輸出格式,
最後再調用父類的handleExceptionInternal方法,將控制權交還給spring,
這樣就完成了整個異常處理的流程
@Override protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { HttpStatus localHttpStatus = status; ErrorResult errorResult = buildError(applicationConfig, ex); if (ex instanceof PermissionException) { //權限異常 localHttpStatus = HttpStatus.FORBIDDEN; } else if (ex instanceof AuthException) { //認證異常 localHttpStatus = HttpStatus.UNAUTHORIZED; } else if (ex instanceof ParameterValidException) { //參數校驗異常 localHttpStatus = HttpStatus.BAD_REQUEST; } else if (ex instanceof RestClientResponseException) { //rest請求異常 try { RestClientResponseException restClientResponseException = (RestClientResponseException) ex; String data = restClientResponseException.getResponseBodyAsString(); if (StringUtils.isNotBlank(data)) { RestResponse<String> child = objectMapper.readValue(data, objectMapper.getTypeFactory().constructParametricType(RestResponse.class, String.class)); errorResult.setChild(child); } } catch (IOException e) { throw new SystemRuntimeException(e); } } log.error(ex.getClass().getName(), ex); return super.handleExceptionInternal(ex, new RestResponse<>(localHttpStatus, errorResult), headers, localHttpStatus, request); }
在上面咱們優化了統一異常處理的代碼,作到了只關心繫統自定義異常的處理,框架和容器的異常處理,交由spring處理,簡化了代碼,避免了重複造輪子,同時代碼也更加健壯了.
在下一篇文章中,我會介紹另一個更合裏的處理404錯誤的方式,敬請期待
想得到最快更新,請關注公衆號