fix(rest-api): preserve status code from ResponseStatusException

This commit is contained in:
Александр Зимин
2026-05-15 15:20:21 +00:00
parent 51b45836de
commit fd35e71fe8
@@ -11,6 +11,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
@@ -112,6 +113,33 @@ public class GlobalExceptionHandler {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
/**
* Preserve status code from {@link ResponseStatusException} (Spring's standard
* way чтобы throw a specific HTTP status from controller). Without explicit
* handler the generic fallback below catches it и turns 401/403/409 в 500 —
* каждый use of throw new ResponseStatusException(...) silently downgraded.
*
* <p>Observed bug: MeNotificationsPreferencesController.requireSub() throws
* 401 anonymous, но клиент получал 500 пока этот handler не появился.
*/
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<ApiErrorResponse> onResponseStatusException(ResponseStatusException e) {
int status = e.getStatusCode().value();
String reason = e.getReason() != null ? e.getReason() : e.getStatusCode().toString();
// Lower-case-snake_case code from status reason — best-effort, falls back
// на 'http_<NNN>' если reason missing/multi-word.
String code = (e.getReason() != null && !e.getReason().contains(" "))
? e.getReason().toLowerCase().replace(' ', '_')
: "http_" + status;
if (status >= 500) {
log.error("ResponseStatusException 5xx: {}", reason, e);
} else {
log.debug("ResponseStatusException {}: {}", status, reason);
}
var body = ApiErrorResponse.of(status, code, reason, currentTraceId());
return ResponseEntity.status(e.getStatusCode()).body(body);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiErrorResponse> onGeneric(Exception e) {
log.error("Unhandled exception in REST API", e);