大型应用程序如何使用 Spring Boot 和 Java 系统地为其所有 API 设置错误消息及其对应的代码,尤其是当它们有超过 100 种与 HTTP 状态代码一致的不同错误消息时?
是否有像@ControllerAdvice这样更通用的方法,而不是处理每个API方法中的每个异常?使用 @ControllerAdvice 有哪些缺点,特别是在异常处理程序的顺序方面?
@GetMapping("/user/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable String id) {
List<UserInfo> userInfoByIdList = null;
try {
userInfoByIdList = userService.getUserById(id);
if (userInfoByIdList.isEmpty()) {
throw new NoUserFoundException("No user found with ID: " + id);
}
if (userInfoByIdList.get(0).isDeleted()) {
throw new UserDeletedException("User with ID: " + id + " has been deleted");
}
if (!userService.isUserEligible(userInfoByIdList.get(0))) {
throw new UserNotEligibleForAccessingUserException("User with ID: " + id + " is not eligible for access");
}
} catch (NoUserFoundException e) {
log.error("NoUserFoundException while fetching user for ID {}: {}", id, e.getMessage(), e);
return new ResponseEntity<>(new UserResponse(HttpStatus.NOT_FOUND.value(), e.getMessage()),
HttpStatus.NOT_FOUND);
} catch (UserDeletedException e) {
log.error("UserDeletedException while fetching user for ID {}: {}", id, e.getMessage(), e);
return new ResponseEntity<>(new UserResponse(HttpStatus.GONE.value(), e.getMessage()),
HttpStatus.GONE);
} catch (UserNotEligibleForAccessingUserException e) {
log.error("UserNotEligibleForAccessingUserException while fetching user for ID {}: {}", id, e.getMessage(), e);
return new ResponseEntity<>(new UserResponse(HttpStatus.FORBIDDEN.value(), e.getMessage()),
HttpStatus.FORBIDDEN);
} catch (ServiceException e) {
log.error("ServiceException while fetching user for ID {}: {}", id, e.getMessage(), e);
return new ResponseEntity<>(new UserResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()),
HttpStatus.INTERNAL_SERVER_ERROR);
} catch (Exception e) {
log.error("Error while fetching user for ID {}: {}", id, e.getMessage(), e);
return new ResponseEntity<>(new UserResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()),
HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(new UserResponse(HttpStatus.OK.value(), userInfoByIdList), HttpStatus.OK);
}
@ControllerAdvice 是大多数时候的选择,因为集中的错误处理和代码的整洁。在你的情况下,它看起来像这样:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NoUserFoundException.class)
public ResponseEntity<ErrorResponse> handleNoUserFoundException(NoUserFoundException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(UserDeletedException.class)
public ResponseEntity<ErrorResponse> handleUserDeletedException(UserDeletedException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.GONE.value(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.GONE);
}
@ExceptionHandler(UserNotEligibleForAccessingUserException.class)
public ResponseEntity<ErrorResponse> handleUserNotEligibleForAccessingUserException(UserNotEligibleForAccessingUserException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.FORBIDDEN.value(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.FORBIDDEN);
}
@ExceptionHandler(ServiceException.class)
public ResponseEntity<ErrorResponse> handleServiceException(ServiceException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
但正如上面提到的,这更多的是一个建议问题,而不是一个技术问题。