package ru.mcs.metadatabook.controller.advice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.OffsetDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Единая точка обработки ошибок публичного API. Без него неизвестное поле
* в ?fields= или невалидный apiKey улетают наружу как HTTP 500 с трассировкой стека,
* что недопустимо для внешнего API.
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, Object>> handleBadRequest(IllegalArgumentException ex) {
return build(HttpStatus.BAD_REQUEST, ex.getMessage());
}
@ExceptionHandler(SecurityException.class)
public ResponseEntity<Map<String, Object>> handleUnauthorized(SecurityException ex) {
return build(HttpStatus.UNAUTHORIZED, ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleGeneric(Exception ex) {
log.error("Unhandled exception in API", ex);
return build(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error");
}
private ResponseEntity<Map<String, Object>> build(HttpStatus status, String message) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", OffsetDateTime.now().toString());
body.put("status", status.value());
body.put("error", status.getReasonPhrase());
body.put("message", message);
return ResponseEntity.status(status).body(body);
}
}