1
0

improved REST-API error handling for broken request body JSON

This commit is contained in:
Michael Hoennig
2022-10-16 10:08:42 +02:00
parent e305c3c935
commit 77ef126a7e
5 changed files with 94 additions and 47 deletions

View File

@ -1,5 +1,6 @@
package net.hostsharing.hsadminng.config;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.openapitools.jackson.nullable.JsonNullableModule;
@ -16,6 +17,7 @@ public class JsonObjectMapperConfiguration {
public Jackson2ObjectMapperBuilder customObjectMapper() {
return new Jackson2ObjectMapperBuilder()
.modules(new JsonNullableModule(), new JavaTimeModule())
.featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}

View File

@ -0,0 +1,51 @@
package net.hostsharing.hsadminng.errors;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.WebRequest;
import java.time.LocalDateTime;
@Getter
class CustomErrorResponse {
static ResponseEntity<CustomErrorResponse> errorResponse(
final WebRequest request,
final HttpStatus httpStatus,
final String message) {
return new ResponseEntity<>(
new CustomErrorResponse(request.getContextPath(), httpStatus, message), httpStatus);
}
static String firstMessageLine(final Throwable exception) {
if (exception.getMessage() != null) {
return firstLine(exception.getMessage());
}
return "ERROR: [500] " + exception.getClass().getName();
}
static String firstLine(final String message) {
return message.split("\\r|\\n|\\r\\n", 0)[0];
}
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
private final LocalDateTime timestamp;
private final String path;
private final int status;
private final String error;
private final String message;
CustomErrorResponse(final String path, final HttpStatus status, final String message) {
this.timestamp = LocalDateTime.now();
this.path = path;
this.status = status.value();
this.error = status.getReasonPhrase();
this.message = message;
}
}

View File

@ -1,13 +1,12 @@
package net.hostsharing.hsadminng.errors;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import org.iban4j.Iban4jException;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.web.bind.MethodArgumentNotValidException;
@ -17,11 +16,12 @@ import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.persistence.EntityNotFoundException;
import java.time.LocalDateTime;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.regex.Pattern;
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
@ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
@ -73,6 +73,16 @@ public class RestResponseEntityExceptionHandler
@Override
@SuppressWarnings("unchecked,rawtypes")
protected ResponseEntity handleExceptionInternal(
Exception exc, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
final var response = super.handleExceptionInternal(exc, body, headers, status, request);
return errorResponse(request, response.getStatusCode(),
Optional.ofNullable(response.getBody()).map(Object::toString).orElse(firstMessageLine(exc)));
}
//@ExceptionHandler({ MethodArgumentNotValidException.class })
@SuppressWarnings("unchecked,rawtypes")
protected ResponseEntity handleMethodArgumentNotValid(
MethodArgumentNotValidException exc,
HttpHeaders headers,
@ -126,45 +136,4 @@ public class RestResponseEntityExceptionHandler
return Optional.empty();
}
private static ResponseEntity<CustomErrorResponse> errorResponse(
final WebRequest request,
final HttpStatus httpStatus,
final String message) {
return new ResponseEntity<>(
new CustomErrorResponse(request.getContextPath(), httpStatus, message), httpStatus);
}
private String firstMessageLine(final Throwable exception) {
if (exception.getMessage() != null) {
return firstLine(exception.getMessage());
}
return "ERROR: [500] " + exception.getClass().getName();
}
private String firstLine(final String message) {
return message.split("\\r|\\n|\\r\\n", 0)[0];
}
}
@Getter
class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
private final LocalDateTime timestamp;
private final String path;
private final int status;
private final String error;
private final String message;
public CustomErrorResponse(final String path, final HttpStatus status, final String message) {
this.timestamp = LocalDateTime.now();
this.path = path;
this.status = status.value();
this.error = status.getReasonPhrase();
this.message = message;
}
}