From d052fd73b3279abe9f828bd305f7bbf00d7e0c30 Mon Sep 17 00:00:00 2001 From: Daniel Gerhardt <code@dgerhardt.net> Date: Thu, 10 Jan 2019 16:07:06 +0100 Subject: [PATCH] Improve handling of Spring's own HTTP Exceptions Slighly refactored ControllerAdvices to include handling of Spring's HTTP Exceptions. This fixes 500 status codes being sent instead of the correct one. Fixes GH-67. --- .../ControllerExceptionHandler.java | 59 +++++++++---------- ...er.java => ControllerExceptionHelper.java} | 6 +- .../DefaultControllerExceptionHandler.java | 10 +++- 3 files changed, 41 insertions(+), 34 deletions(-) rename src/main/java/de/thm/arsnova/controller/{AbstractControllerExceptionHandler.java => ControllerExceptionHelper.java} (86%) diff --git a/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java index 2016f8def..29b76fcaf 100644 --- a/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java +++ b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java @@ -27,19 +27,20 @@ import de.thm.arsnova.web.exceptions.PreconditionFailedException; import de.thm.arsnova.web.exceptions.UnauthorizedException; import org.ektorp.DocumentNotFoundException; import org.slf4j.event.Level; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; -import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.servlet.NoHandlerFoundException; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import javax.naming.OperationNotSupportedException; import javax.servlet.http.HttpServletRequest; @@ -50,40 +51,39 @@ import java.util.Map; * Translates exceptions into HTTP status codes. */ @ControllerAdvice -public class ControllerExceptionHandler extends AbstractControllerExceptionHandler { +public class ControllerExceptionHandler extends ResponseEntityExceptionHandler { + private ControllerExceptionHelper helper; + + public ControllerExceptionHandler(final ControllerExceptionHelper helper) { + this.helper = helper; + } + @ExceptionHandler(NoContentException.class) @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) public Map<String, Object> handleNoContentException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.TRACE); - } - - @ExceptionHandler(NoHandlerFoundException.class) - @ResponseBody - @ResponseStatus(HttpStatus.NOT_FOUND) - public Map<String, Object> handleNoHandlerFoundException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.TRACE); + return helper.handleException(e, Level.TRACE); } @ExceptionHandler(NotFoundException.class) @ResponseBody @ResponseStatus(HttpStatus.NOT_FOUND) public Map<String, Object> handleNotFoundException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.TRACE); + return helper.handleException(e, Level.TRACE); } @ExceptionHandler(UnauthorizedException.class) @ResponseBody @ResponseStatus(HttpStatus.UNAUTHORIZED) public Map<String, Object> handleUnauthorizedException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.TRACE); + return helper.handleException(e, Level.TRACE); } @ExceptionHandler(AuthenticationCredentialsNotFoundException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) @ResponseBody public Map<String, Object> handleAuthenticationCredentialsNotFoundException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } @ExceptionHandler(AccessDeniedException.class) @@ -102,49 +102,42 @@ public class ControllerExceptionHandler extends AbstractControllerExceptionHandl response.setStatus(HttpStatus.FORBIDDEN.value()); } - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } @ExceptionHandler(ForbiddenException.class) @ResponseBody @ResponseStatus(HttpStatus.FORBIDDEN) public Map<String, Object> handleForbiddenException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } - @ExceptionHandler({BadRequestException.class, HttpRequestMethodNotSupportedException.class}) + @ExceptionHandler(BadRequestException.class) @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public Map<String, Object> handleBadRequestException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } @ExceptionHandler(PreconditionFailedException.class) @ResponseBody @ResponseStatus(HttpStatus.PRECONDITION_FAILED) public Map<String, Object> handlePreconditionFailedException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } @ExceptionHandler({NotImplementedException.class, OperationNotSupportedException.class}) @ResponseBody @ResponseStatus(HttpStatus.NOT_IMPLEMENTED) public Map<String, Object> handleNotImplementedException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } @ExceptionHandler(PayloadTooLargeException.class) @ResponseBody @ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE) public Map<String, Object> handlePayloadTooLargeException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); - } - - @ExceptionHandler(HttpMessageNotReadableException.class) - @ResponseBody - @ResponseStatus(HttpStatus.BAD_REQUEST) - public Map<String, Object> handleHttpMessageNotReadableException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.DEBUG); + return helper.handleException(e, Level.DEBUG); } /* FIXME: Wrap persistance Exceptions - do not handle persistance Exceptions at the controller layer */ @@ -152,6 +145,12 @@ public class ControllerExceptionHandler extends AbstractControllerExceptionHandl @ResponseBody @ResponseStatus(HttpStatus.NOT_FOUND) public Map<String, Object> handleDocumentNotFoundException(final Exception e, final HttpServletRequest request) { - return handleException(e, Level.TRACE); + return helper.handleException(e, Level.TRACE); + } + + @Override + protected ResponseEntity<Object> handleExceptionInternal(final Exception ex, final Object body, + final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + return new ResponseEntity<>(helper.handleException(ex, Level.TRACE), headers, status); } } diff --git a/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHelper.java similarity index 86% rename from src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java rename to src/main/java/de/thm/arsnova/controller/ControllerExceptionHelper.java index 58e3f907e..50abc01c0 100644 --- a/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java +++ b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHelper.java @@ -5,12 +5,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; -public class AbstractControllerExceptionHandler { - private static final Logger logger = LoggerFactory.getLogger(AbstractControllerExceptionHandler.class); +@Component +public class ControllerExceptionHelper { + private static final Logger logger = LoggerFactory.getLogger(ControllerExceptionHelper.class); /* Since exception messages might contain sensitive data, they are not exposed by default. */ @Value("${api.expose-error-messages:false}") private boolean exposeMessages; diff --git a/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java index 3d1249280..a788aa791 100644 --- a/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java +++ b/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java @@ -12,7 +12,13 @@ import javax.servlet.http.HttpServletRequest; import java.util.Map; @ControllerAdvice -public class DefaultControllerExceptionHandler extends AbstractControllerExceptionHandler { +public class DefaultControllerExceptionHandler { + private ControllerExceptionHelper helper; + + public DefaultControllerExceptionHandler(final ControllerExceptionHelper helper) { + this.helper = helper; + } + @ExceptionHandler @ResponseBody @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @@ -27,6 +33,6 @@ public class DefaultControllerExceptionHandler extends AbstractControllerExcepti throw e; } - return handleException(e, Level.ERROR); + return helper.handleException(e, Level.ERROR); } } -- GitLab