По мотивам статьи Изучаем ResponseEntity<?> и избавляемся от него в контроллерах Spring Boot
В заметке рассмотрено 2 способа:
1. Способ с возвратом ResponseEntity<?>
2. Способ с возвратом объекта
1. Способ с возвратом ResponseEntity<?>
Возвращается ResponseEntity<?>.
@GetMapping("/{message}")
public ResponseEntity<?> echo(@PathVariable("message") String message) {
if (message == null || message.isEmpty() || message.equals("test_error")) {
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("message is empty"); // error cod 502
}
if (message.length() < 2) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body("message is too short"); // error cod 503
} else {
return ResponseEntity.ok(new SimpleObj(message));
}
}
ResponseEntity<?> - это обертка над HttpStatus и Object. Возвращается в любом случае, даже если объект не найден. Т.е. можно не обрабатывать исключения. Для более полного описания ошибки можно сделать так (с помощью AppError):
@GetMapping("/products")
public ResponseEntity<?> getProduct(Long id){
try {
Product product = productsService.findById(id).orElseThrow();
return new ResponseEntity<>(product, HttpStatus.OK); // Возврат объекта
} catch (Exception e) {
return new ResponseEntity<>(
new AppError(
HttpStatus.NOT_FOUND.value(),
"Product with id " + id + " not found"),HttpStatus.NOT_FOUND
);
}
}
где AppError это простой класс:
public class AppError {
private int statusCode;
private String message;
public AppError() {}
public AppError(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Тестировать с помощью ResponseEntity. Некрасиво, что возвращается в виде ResponseEntity<?>.
Пример https://github.com/cherepakhin/response_entity
2. Способ с возвратом объекта
@RestController
@RequestMapping("/simpleobj")
public class SimpleObjRest {
@GetMapping("/{message}")
public SimpleObj getMessageWithExcption(@PathVariable("message") String name) throws MyResourceException {
if (name.equals("error")) {
throw new MyResourceException("Exception for name=error");
}
return new SimpleObj(name);
}
}
где MyResourceException.java это класс исключения:
public class MyResourceException extends Throwable {
public MyResourceException(String message) {
super(message);
}
}
и обработка MyResourceException через Advice и Handler в классе GlobalExceptionHandler.java:
@ControllerAdvice // <---------------- Advice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { // <--------- Handler
private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler
public ResponseEntity catchResourceNotFoundException(MyResourceException e) { // <---- MyResourceException !!!
logger.error("GlobalExceptionHandler.catchResourceNotFoundException");
logger.error(e.getMessage(), e);
return new ResponseEntity(new AppError(e.getMessage()), HttpStatus.SERVICE_UNAVAILABLE); // <- AppError !!!
}
}
где AppError.java:
public class AppError {
private String message;
public AppError() {
this("Internal Server Error");
}
public AppError(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AppError)) return false;
AppError appError = (AppError) o;
return Objects.equals(message, appError.message);
}
@Override
public int hashCode() {
return Objects.hashCode(message);
}
}
(про ResponseBody см. https://www.baeldung.com/spring-request-response-body).
Просто тестировать.
Пример https://github.com/cherepakhin/object_and_excp