內容目錄
Spring boot Exception Handling For REST
本文是使用 @ControllerAdvice annotation 來統一管理 try…catch 的異常錯誤處理。
axios 的設定
對於 axios 作一個統一錯誤回饋的設定: errorHandle
import Vue from 'vue'; import router from '../router' import axios from 'axios' import i18n from '../i18n' const errorHandle = async (status, msg) => { switch (status) { case 400: console.log("400") Vue.prototype.$toast.add({severity:'error', summary: i18n.t('HttpStatus.400'), detail: msg.error, life: 3000}); break; case 401: console.log("401") sessionStorage.clear await Vue.prototype.$toast.add({severity:'error', summary: i18n.t('HttpStatus.401'), detail: msg.error , life: 3000}); setTimeout(() => { router.push("/Login"); }, 4000); break; case 403: console.log("403") await Vue.prototype.$toast.add({severity:'error', summary: i18n.t('HttpStatus.403'), detail: msg.error , life: 3000}); setTimeout(() => { router.push("/Login"); }, 4000); break; case 409: console.log("409") await Vue.prototype.$toast.add({severity:'error', summary: i18n.t('HttpStatus.409'), detail: msg.message , life: 3000}); break; case 500: console.log("500") await Vue.prototype.$toast.add({severity:'error', summary: i18n.t('HttpStatus.500'), detail: msg.message , life: 3000}); break; default: console.log(status) await Vue.prototype.$toast.add({severity:'error', summary: msg , detail: msg.message , life: 3000}); break; } }
axios 對於 response 的 interceptors
將定義好的 errorHandle
放入,這樣 axios 就設定好了
// doing something with the response axios.interceptors.response.use( (response) => { return response; }, (error) => { const {response} = error; if (response) { // 成功發出請求且收到 response, 但有 error errorHandle(response.status, response.data); return Promise.reject(error); } } );
使用 @ControllerAdvice annotation
RestResponseEntityExceptionHandler
建立一個 class: RestResponseEntityExceptionHandler ,因為是針對 REST 的錯誤來作處理,所以要 extends ResponseEntityExceptionHandler
,在 Spring boot 使用 @ControllerAdvice
來捕捉錯誤,並利用 @ExceptionHandler
針對 Exception.class
進行捕捉。這裡是對 Conflict (資料重覆) 時作特別處理。
註: @ControllerAdvice是在Spring 3.2新增的annotation,可以用來攔截並處理應用程式中全部Controller所拋出的Exception例外錯誤。其也是
@Component
,所以會被Spring scan為Bean。
@ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @Autowired ResourceBundleMessageSource messageSource; @ExceptionHandler(Exception.class) protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) { String bodyOfResponse = "資料重覆"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); } }
原始程式範例
在RestController中,當發生 Conflict (資料重覆) ,印出錯誤訊息後e.printStackTrace(); ,就會到 RestResponseEntityExceptionHandler.class 中的 handleConflict() 了
@PostMapping("role/save") public ResponseEntity<?> saveRole(@RequestBody @Valid Authority role, HttpServletRequest request) { Map<String,Object> responseJson = new HashMap<String,Object>(); Map<String,Object> responseError = new HashMap<String,Object>(); Authority newEntity = new Authority(); try { boolean isSystem = Objects.isNull( role.getIsSystem() ) ? false : role.getIsSystem(); boolean isEnabled = Objects.isNull( role.getEnabled() ) ? false : role.getEnabled(); role.setEnabled(isEnabled); role.setIsSystem(isSystem); role.setCreateDate(sysService.getNowTimestamp()); role.setCreateUser(userService.getCurrentUser().getId()); newEntity = roleRepo.save(role); responseJson.put("role", newEntity); } catch (Exception e) { e.printStackTrace(); } if (responseError.isEmpty()) { return ResponseEntity .ok() .contentType(MediaType.APPLICATION_JSON) .body(responseJson); } else { responseJson.put("error", responseError); return ResponseEntity .badRequest() .contentType(MediaType.APPLICATION_JSON) .body(responseJson); } }
程式改寫
上面的作法, 仍是用 try...catch
到 Exception 才到 RestResponseEntityExceptionHandler ,這仍不是我們想要的結果,應該是在 controller 裡寫正常的商業交易,發生問題時統一到 RestResponseEntityExceptionHandler 。因此,改寫一下作法如下:
/** * 統一異常處理 * @author polin.wei * */ @ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired ResourceBundleMessageSource messageSource; /** * 新增/修改 時發生資料重覆的異常處理 * @param ex * @param request * @return */ @ExceptionHandler(DataIntegrityViolationException.class) protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) { String bodyOfResponse = messageSource.getMessage("page.text.curd.conflict", null, request.getLocale()); if (logger.isDebugEnabled()) { ex.printStackTrace(); } return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); } @ExceptionHandler(NullPointerException.class) protected ResponseEntity<?> handleNullPointerException(RuntimeException ex, WebRequest request) { String bodyOfResponse = messageSource.getMessage("page.msg.nullPointerException", null, request.getLocale()); if (logger.isDebugEnabled()) { ex.printStackTrace(); } return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); } /** * 其它的異常處理 * @param ex * @param request * @return */ @ExceptionHandler(Exception.class) protected ResponseEntity<Object> handleException(RuntimeException ex, WebRequest request) { String bodyOfResponse = messageSource.getMessage("主機作業異常, 請連繫資訊單位", null, request.getLocale()); if (logger.isDebugEnabled()) { ex.printStackTrace(); } return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR , request); } }
這時候, controller 就可以不用再寫 try...catch
了
@PostMapping("role/save") public ResponseEntity<?> saveRole(@RequestBody @Valid Authority role, HttpServletRequest request, Locale locale) { responseJson = new HashMap<String, Object>(); responseError = new HashMap<String, Object>(); Authority newEntity = new Authority(); boolean isSystem = Objects.isNull(role.getIsSystem()) ? false : role.getIsSystem(); boolean isEnabled = Objects.isNull(role.getEnabled()) ? false : role.getEnabled(); role.setEnabled(isEnabled); role.setIsSystem(isSystem); role.setCreateDate(sysService.getNowTimestamp()); role.setCreateUser(userService.getCurrentUser().getId()); newEntity = roleRepo.save(role); responseJson.put("role", newEntity); responseJson.put("message", messageSource.getMessage(super.CRUD_SUCCESS, null, locale)); return super.doResponse(); }
參考:
你必須 登入 才能發表評論。