Exception handling #82

Merged
stne3960 merged 6 commits from feature/exception-handling into main 2026-02-02 11:18:37 +01:00
5 changed files with 156 additions and 143 deletions
Showing only changes of commit 9e63eb6406 - Show all commits

View File

@ -1,5 +1,9 @@
package se.su.dsv.studentportalen.bff.exception; package se.su.dsv.studentportalen.bff.exception;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -9,11 +13,6 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* Centralized exception handler for all REST controllers. * Centralized exception handler for all REST controllers.
* Returns RFC 7807 Problem Details for all error responses. * Returns RFC 7807 Problem Details for all error responses.
@ -21,7 +20,9 @@ import java.util.stream.Collectors;
@RestControllerAdvice @RestControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final Logger LOG = LoggerFactory.getLogger(
GlobalExceptionHandler.class
);
/** /**
* Handles Jakarta Bean Validation errors (missing/invalid fields). * Handles Jakarta Bean Validation errors (missing/invalid fields).
@ -29,14 +30,22 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(MethodArgumentNotValidException.class) @ExceptionHandler(MethodArgumentNotValidException.class)
public ProblemDetail handleValidation(MethodArgumentNotValidException ex) { public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
Map<String, List<String>> violations = ex.getBindingResult().getFieldErrors().stream() Map<String, List<String>> violations = ex
.collect(Collectors.groupingBy( .getBindingResult()
.getFieldErrors()
.stream()
.collect(
Collectors.groupingBy(
FieldError::getField, FieldError::getField,
Collectors.mapping( Collectors.mapping(
error -> error.getDefaultMessage() != null ? error.getDefaultMessage() : "Invalid value", error ->
error.getDefaultMessage() != null
? error.getDefaultMessage()
: "Invalid value",
Collectors.toList() Collectors.toList()
) )
)); )
);
LOG.warn("Validation failed: {}", violations); LOG.warn("Validation failed: {}", violations);
@ -76,7 +85,9 @@ public class GlobalExceptionHandler {
public ProblemDetail handleUnexpected(Exception ex) { public ProblemDetail handleUnexpected(Exception ex) {
LOG.error("Unexpected error", ex); LOG.error("Unexpected error", ex);
ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); ProblemDetail problem = ProblemDetail.forStatus(
HttpStatus.INTERNAL_SERVER_ERROR
);
problem.setType(URI.create("studentportalen:internal-error")); problem.setType(URI.create("studentportalen:internal-error"));
problem.setTitle("An unexpected error occurred"); problem.setTitle("An unexpected error occurred");
problem.setDetail("Please try again later"); problem.setDetail("Please try again later");

View File

@ -1,9 +1,8 @@
package se.su.dsv.studentportalen.bff.exception; package se.su.dsv.studentportalen.bff.exception;
import org.springframework.http.HttpStatus;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.http.HttpStatus;
/** /**
* Exception for validation errors, including field-level violations. * Exception for validation errors, including field-level violations.
@ -17,7 +16,10 @@ public class ValidationException extends StudentportalenException {
this(message, Map.of()); this(message, Map.of());
} }
public ValidationException(String message, Map<String, List<String>> violations) { public ValidationException(
String message,
Map<String, List<String>> violations
) {
super(message); super(message);
this.violations = violations != null ? Map.copyOf(violations) : Map.of(); this.violations = violations != null ? Map.copyOf(violations) : Map.of();
} }