您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“如何處理Spring MVC Controller返回值及異常”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“如何處理Spring MVC Controller返回值及異常”這篇文章吧。
舊的設計方案
開發api的時候,需要先定義好接口的數據響應結果.如下是一個很簡單直接的Controller實現方法及響應結果定義.
@RestController @RequestMapping("/users") public class UserController { @Inject private UserService userService; @GetRequest("/{userId:\\d+}") public ResponseBean signin(@PathVariable long userId) { try { User user = userService.getUserBaseInfo(userId); return ResponseBean.success(user); } catch (ServiceException e) { return new ReponseBean(e.getCode(), e.getMsg()); } catch (Exception e) { return ResponseBean.systemError(); } } }
{ code: "", data: {}, // 可以是對象或者數組 msg: "" }
從上面的代碼,我們可以看到對于每個 Controller 方法,都會有很多重復的代碼出現,我們應該設法去避免重復的代碼。將重復的代碼移除之后,可以得到如下的代碼,簡單易懂。
@RestController @RequestMapping("/users") public class UserController { @Inject private UserService userService; @GetRequest("/{userId:\\d+}") public User signin(@PathVariable long userId) { return userService.getUserBaseInfo(userId); } }
在以上的實現中,還做了一個必要的要求,就是 ServiceException 需要定義為 RuntimeException的子類,而不是 Exception的子類。由于 ServiceException 表示服務異常,一般發生這種異常是應該直接提示前端,而無需進行其他特殊處理的。在定義為 RuntimeException 的子類之后,會減少大量的異常拋出聲明,而且不再需要在事務@Transactional 中進行特殊聲明。
統一 Controller 返回值格式
在開發的過程中,我發現上面的結構
@ControllerAdvice public class ControllerResponseHandler implements ResponseBodyAdvice<Object> { private Logger logger = LogManager.getLogger(getClass()); @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 支持所有的返回值類型 return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if(body instanceof ResponseBean) { return body; } else { // 所有沒有返回 ResponseBean 結構的結果均認為是成功的 return ResponseBean.success(body); } } }
統一異常處理
如下的代碼中,ServiceException ServiceMessageException ValidatorErrorType FieldValidatorError
均為自定義類。
@ControllerAdvice public class ControllerExceptionHandler { private Logger logger = LogManager.getLogger(getClass()); private static final String logExceptionFormat = "[EXIGENCE] Some thing wrong with the system: %s"; /** * 自定義異常 */ @ExceptionHandler(ServiceMessageException.class) public ResponseBean handleServiceMessageException(HttpServletRequest request, ServiceMessageException ex) { logger.debug(ex); return new ResponseBean(ex.getMsgCode(), ex.getMessage()); } /** * 自定義異常 */ @ExceptionHandler(ServiceException.class) public ResponseBean handleServiceException(HttpServletRequest request, ServiceException ex) { logger.debug(ex); String message = codeToMessage(ex.getMsgCode()); return new ResponseBean(ex.getMsgCode(), message); } /** * MethodArgumentNotValidException: 實體類屬性校驗不通過 * 如: listUsersValid(@RequestBody @Valid UserFilterOption option) */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseBean handleMethodArgumentNotValid(HttpServletRequest request, MethodArgumentNotValidException ex) { logger.debug(ex); return validatorErrors(ex.getBindingResult()); } private ResponseBean validatorErrors(BindingResult result) { List<FieldValidatorError> errors = new ArrayList<FieldValidatorError>(); for (FieldError error : result.getFieldErrors()) { errors.add(toFieldValidatorError(error)); } return ResponseBean.validatorError(errors); } /** * ConstraintViolationException: 直接對方法參數進行校驗,校驗不通過。 * 如: pageUsers(@RequestParam @Min(1)int pageIndex, @RequestParam @Max(100)int pageSize) */ @ExceptionHandler(ConstraintViolationException.class) public ResponseBean handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) { logger.debug(ex); // List<FieldValidatorError> errors = new ArrayList<FieldValidatorError>(); for (ConstraintViolation<?> violation : ex.getConstraintViolations()) { errors.add(toFieldValidatorError(violation)); } return ResponseBean.validatorError(errors); } private FieldValidatorError toFieldValidatorError(ConstraintViolation<?> violation) { Path.Node lastNode = null; for (Path.Node node : violation.getPropertyPath()) { lastNode = node; } FieldValidatorError fieldNotValidError = new FieldValidatorError(); // fieldNotValidError.setType(ValidatorTypeMapping.toType(violation.getConstraintDescriptor().getAnnotation().annotationType())); fieldNotValidError.setType(ValidatorErrorType.INVALID.value()); fieldNotValidError.setField(lastNode.getName()); fieldNotValidError.setMessage(violation.getMessage()); return fieldNotValidError; } private FieldValidatorError toFieldValidatorError(FieldError error) { FieldValidatorError fieldNotValidError = new FieldValidatorError(); fieldNotValidError.setType(ValidatorErrorType.INVALID.value()); fieldNotValidError.setField(error.getField()); fieldNotValidError.setMessage(error.getDefaultMessage()); return fieldNotValidError; } /** * BindException: 數據綁定異常,效果與MethodArgumentNotValidException類似,為MethodArgumentNotValidException的父類 */ @ExceptionHandler(BindException.class) public ResponseBean handleBindException(HttpServletRequest request, BindException ex) { logger.debug(ex); return validatorErrors(ex.getBindingResult()); } /** * 返回值類型轉化錯誤 */ @ExceptionHandler(HttpMessageConversionException.class) public ResponseBean exceptionHandle(HttpServletRequest request, HttpMessageConversionException ex) { return internalServiceError(ex); } /** * 對應 Http 請求頭的 accept * 客戶器端希望接受的類型和服務器端返回類型不一致。 * 這里雖然設置了攔截,但是并沒有起到作用。需要通過http請求的流程來進一步確定原因。 */ @ExceptionHandler(HttpMediaTypeNotAcceptableException.class) public ResponseBean handleHttpMediaTypeNotAcceptableException(HttpServletRequest request, HttpMediaTypeNotAcceptableException ex) { logger.debug(ex); StringBuilder messageBuilder = new StringBuilder().append("The media type is not acceptable.") .append(" Acceptable media types are "); ex.getSupportedMediaTypes().forEach(t -> messageBuilder.append(t + ", ")); String message = messageBuilder.substring(0, messageBuilder.length() - 2); return new ResponseBean(HttpStatus.NOT_ACCEPTABLE.value(), message); } /** * 對應請求頭的 content-type * 客戶端發送的數據類型和服務器端希望接收到的數據不一致 */ @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseBean handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException ex) { logger.debug(ex); StringBuilder messageBuilder = new StringBuilder().append(ex.getContentType()) .append(" media type is not supported.").append(" Supported media types are "); ex.getSupportedMediaTypes().forEach(t -> messageBuilder.append(t + ", ")); String message = messageBuilder.substring(0, messageBuilder.length() - 2); System.out.println(message); return new ResponseBean(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), message); } /** * 前端發送過來的數據無法被正常處理 * 比如后天希望收到的是一個json的數據,但是前端發送過來的卻是xml格式的數據或者是一個錯誤的json格式數據 */ @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseBean handlerHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException ex) { logger.debug(ex); String message = "Problems parsing JSON"; return new ResponseBean(HttpStatus.BAD_REQUEST.value(), message); } /** * 將返回的結果轉化到響應的數據時候導致的問題。 * 當使用json作為結果格式時,可能導致的原因為序列化錯誤。 * 目前知道,如果返回一個沒有屬性的對象作為結果時,會導致該異常。 */ @ExceptionHandler(HttpMessageNotWritableException.class) public ResponseBean handlerHttpMessageNotWritableException(HttpServletRequest request, HttpMessageNotWritableException ex) { return internalServiceError(ex); } /** * 請求方法不支持 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseBean exceptionHandle(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) { logger.debug(ex); StringBuilder messageBuilder = new StringBuilder().append(ex.getMethod()) .append(" method is not supported for this request.").append(" Supported methods are "); ex.getSupportedHttpMethods().forEach(m -> messageBuilder.append(m + ",")); String message = messageBuilder.substring(0, messageBuilder.length() - 2); return new ResponseBean(HttpStatus.METHOD_NOT_ALLOWED.value(), message); } /** * 參數類型不匹配 */ @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseBean methodArgumentTypeMismatchExceptionHandler(HttpServletRequest request, MethodArgumentTypeMismatchException ex) { logger.debug(ex); String message = "The parameter '" + ex.getName() + "' should of type '" + ex.getRequiredType().getSimpleName().toLowerCase() + "'"; FieldValidatorError fieldNotValidError = new FieldValidatorError(); fieldNotValidError.setType(ValidatorErrorType.TYPE_MISMATCH.value()); fieldNotValidError.setField(ex.getName()); fieldNotValidError.setMessage(message); return ResponseBean.validatorError(Arrays.asList(fieldNotValidError)); } /** * 缺少必填字段 */ @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseBean exceptionHandle(HttpServletRequest request, MissingServletRequestParameterException ex) { logger.debug(ex); String message = "Required parameter '" + ex.getParameterName() + "' is not present"; FieldValidatorError fieldNotValidError = new FieldValidatorError(); fieldNotValidError.setType(ValidatorErrorType.MISSING_FIELD.value()); fieldNotValidError.setField(ex.getParameterName()); fieldNotValidError.setMessage(message); return ResponseBean.validatorError(Arrays.asList(fieldNotValidError)); } /** * 文件上傳時,缺少 file 字段 */ @ExceptionHandler(MissingServletRequestPartException.class) public ResponseBean exceptionHandle(HttpServletRequest request, MissingServletRequestPartException ex) { logger.debug(ex); return new ResponseBean(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); } /** * 請求路徑不存在 */ @ExceptionHandler(NoHandlerFoundException.class) public ResponseBean exceptionHandle(HttpServletRequest request, NoHandlerFoundException ex) { logger.debug(ex); String message = "No resource found for " + ex.getHttpMethod() + " " + ex.getRequestURL(); return new ResponseBean(HttpStatus.NOT_FOUND.value(), message); } /** * 缺少路徑參數 * Controller方法中定義了 @PathVariable(required=true) 的參數,但是卻沒有在url中提供 */ @ExceptionHandler(MissingPathVariableException.class) public ResponseBean exceptionHandle(HttpServletRequest request, MissingPathVariableException ex) { return internalServiceError(ex); } /** * 其他所有的異常 */ @ExceptionHandler() public ResponseBean handleAll(HttpServletRequest request, Exception ex) { return internalServiceError(ex); } private String codeToMessage(int code) { //TODO 這個需要進行自定,每個 code 會匹配到一個相應的 msg return "The code is " + code; } private ResponseBean internalServiceError(Exception ex) { logException(ex); // do something else return ResponseBean.systemError(); } private <T extends Throwable> void logException(T e) { logger.error(String.format(logExceptionFormat, e.getMessage()), e); } }
通過上面的配置,可以有效地將異常進行統一的處理,同時對返回的結果進行統一的封裝。
以上是“如何處理Spring MVC Controller返回值及異常”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。