Global error handling
This commit is contained in:
86
src/main/java/se/urmo/hemhub/web/ErrorHandling.java
Normal file
86
src/main/java/se/urmo/hemhub/web/ErrorHandling.java
Normal file
@ -0,0 +1,86 @@
|
||||
package se.urmo.hemhub.web;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class ErrorHandling {
|
||||
|
||||
record ErrorResponse(Instant timestamp, int status, String error, String message, String path, Map<String,Object> details){}
|
||||
|
||||
private ErrorResponse resp(HttpStatus s, String message, String path, Map<String,Object> details) {
|
||||
return new ErrorResponse(Instant.now(), s.value(), s.getReasonPhrase(), message, path, details);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
ErrorResponse handleValidation(MethodArgumentNotValidException ex, HttpServletRequest req) {
|
||||
var fieldErrors = ex.getBindingResult().getFieldErrors().stream()
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
fe -> fe.getField(),
|
||||
fe -> fe.getDefaultMessage() != null ? fe.getDefaultMessage() : "Invalid value",
|
||||
(a,b) -> a));
|
||||
return resp(HttpStatus.BAD_REQUEST, "Validation failed", req.getRequestURI(), Map.of("fields", fieldErrors));
|
||||
}
|
||||
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
ErrorResponse handleConstraintViolation(ConstraintViolationException ex, HttpServletRequest req) {
|
||||
var errs = ex.getConstraintViolations().stream()
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
v -> v.getPropertyPath().toString(),
|
||||
v -> v.getMessage(),
|
||||
(a,b)->a));
|
||||
return resp(HttpStatus.BAD_REQUEST, "Validation failed", req.getRequestURI(), Map.of("violations", errs));
|
||||
}
|
||||
|
||||
@ExceptionHandler({ HttpMessageNotReadableException.class })
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
ErrorResponse handleBadBody(Exception ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.BAD_REQUEST, "Malformed request body", req.getRequestURI(), Map.of());
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoSuchElementException.class)
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
ErrorResponse handleNotFound(NoSuchElementException ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.NOT_FOUND, "Not found", req.getRequestURI(), Map.of());
|
||||
}
|
||||
|
||||
@ExceptionHandler({ SecurityException.class, AccessDeniedException.class })
|
||||
@ResponseStatus(HttpStatus.FORBIDDEN)
|
||||
ErrorResponse handleForbidden(Exception ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.FORBIDDEN, "Forbidden", req.getRequestURI(), Map.of());
|
||||
}
|
||||
|
||||
@ExceptionHandler(DataIntegrityViolationException.class)
|
||||
@ResponseStatus(HttpStatus.CONFLICT)
|
||||
ErrorResponse handleConflict(DataIntegrityViolationException ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.CONFLICT, "Conflict", req.getRequestURI(), Map.of());
|
||||
}
|
||||
|
||||
@ExceptionHandler({ HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class })
|
||||
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
ErrorResponse handleMethod(Exception ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.METHOD_NOT_ALLOWED, "Method not allowed", req.getRequestURI(), Map.of());
|
||||
}
|
||||
|
||||
// Fallback (kept last)
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
ErrorResponse handleOther(Exception ex, HttpServletRequest req) {
|
||||
return resp(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error", req.getRequestURI(),
|
||||
Map.of("hint","Check server logs"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user