Spring boot unified error handling
It is expected to define a specification that makes it very convenient for the front end to handle interface errors. So the problem is, how can the front end handle the interface more conveniently?
The front-end scaffold usually writes an interceptor for all API requests, and one of the most important capabilities of the interceptor is to preprocess the data. Assuming that all correct data goes to the success handle of ajax and all abnormal data goes to the error handle, the results of success or failure will not be interspersed with each other. We can undoubtedly save a lot of if else branches when defining the handle.
This is the first and second: all successful data are in ResultEnry format, and all errors are errorcode+errormsg hash. In the front-end interceptor, the error handle can be configured with error code linkage error information, which is very convenient for error reporting pop-up window and saves a large number of Logic Codes of interface class exceptions.
Therefore, the unified error handling here should meet two requirements:
- The background interface should not catch and handle data exceptions
- Unified format of exceptions thrown to the front end
Custom return entity
Slightly. Refer to another article Spring boot custom return entity.
Two methods of unified exception handling
- Using annotation ControllerAdvice
- Implement ErrorController
In this example, ControllerAdvice + custom exception is selected to realize unified specification. Two methods are briefly introduced below.
ControllerAdvice
This annotation defines an exception class, which defines a variety of exception handles.
@ControllerAdvice public class WebExceptionHandler { @ExceptionHandler public Result<Object> unknownException(Exception e) { return ResultEntry.error(ResultEnum.UNKNOWN_ERROR); } @ExceptionHandler public Result<Object> ioException(IOException e) { return ResultEntry.error(ResultEnum.UNKNOWN_ERROR); } }
This method can only listen for exceptions thrown by the controller
ErrorController
This method will invalidate the default BaseErrorController of the framework
@RestController public class WebExceptionHandler implements ErrorController { public Result<Object> error(HttpServletRequest request, HttpServletResponse response) { return ResultEntry.error(ResultEnum.UNKNOWN_ERROR); } }
Custom runtime exception
The business code catches exceptions, and then throws them up layer by layer through custom exceptions, and enumerates ResultEnum in combination with the status code to achieve a unified format and accurate prompt.
Custom exception ServiceException:
public class ServiceException extends RuntimeException{ private ResultEnum error; public ServiceException(ResultEnum error) { this.error = error; } public ResultEnum getError() { return error; } public void setError(ResultEnum error) { this.error = error; } }
ControllerAdvice exception handle
@ControllerAdvice public class ServiceExceptionHander { @ExceptionHandler(ServiceException.class) @ResponseBody public <T> ResponseEntity<Result<T>> handle(ServiceException e){ ResponseEntity<Result<T>> result = ResultEntry.error(e.getError()); return result; } }
ResultEntry assembles the returned data structure and uses ResponseEntity to pass it to the front-end error code and error entity. So if the interface can get the correct data, it returns httpstatus OK, otherwise httpstatus is returned BAD_ GATEWAY; The front end will also trigger the success or error handle of ajax accordingly.
public class ResultEntry<T> implements Serializable{ public static <T> ResponseEntity<Result<T>> success(T o) { return ResultEntry.response(ResultEnum.SUCCESS, o); } public static <T> ResponseEntity<Result<T>> success() { return ResultEntry.response(ResultEnum.SUCCESS); } public static <T> ResponseEntity<Result<T>> response(ResultEnum enu, T o) { Result<T> result = new Result<T>(); result.setMsg(enu.getMsg()); result.setStatus(enu.getStatus()); result.setBody(o); return new ResponseEntity<Result<T>>(result, HttpStatus.OK); } public static <T> ResponseEntity<Result<T>> response(ResultEnum enu) { Result<T> result = new Result<T>(); result.setMsg(enu.getMsg()); result.setStatus(enu.getStatus()); return new ResponseEntity<Result<T>>(HttpStatus.OK); } public static <T> ResponseEntity<Result<T>> error(ResultEnum enu) { Result<T> result = new Result<T>(); result.setMsg(enu.getMsg()); result.setStatus(enu.getStatus()); return new ResponseEntity<Result<T>>(result, HttpStatus.BAD_GATEWAY); } }
test
//controller @RestController() public class UpgradeController { @Autowired private UpgradeService upgradeService; @RequestMapping(value = "upgrade/records", method = RequestMethod.GET) public ResponseEntity<Result<ResultPage<UpgradeRecordDTO>>> getUpgradeRecords() { ResultPage<UpgradeRecordDTO> data = upgradeService.getUpgradeRecords(); return ResultEntry.success(data); } } //service @Service public class UpgradeService { @Autowired private UpgradeRecordRepository recordRepository; // Check upgrade records public ResultPage<UpgradeRecordDTO> getUpgradeRecords() throws ServiceException { Pageable pageable = PageRequest.of(0, 10); return new ResultPage<>(recordRepository.findAll(pageable)); } }