From 22e98e06eafae25dbbf7a9eaaf7e858d4b11d519 Mon Sep 17 00:00:00 2001 From: Ger Roza Date: Tue, 15 Jan 2019 18:45:36 -0200 Subject: [PATCH 1/4] Removed most exception handlers from spring-rest-full (except the ones used in other articles) and moved them to spring-boot-rest --- .../RestResponseEntityExceptionHandler.java | 86 +++++++++++++++++++ .../web/exception/MyDataAccessException.java | 21 +++++ .../MyDataIntegrityViolationException.java | 21 +++++ .../MyResourceNotFoundException.java | 21 +++++ spring-rest-full/.attach_pid28499 | 0 spring-rest-full/pom.xml | 43 ---------- .../RestResponseEntityExceptionHandler.java | 61 +------------ ...{TestSuite.java => TestSuiteLiveTest.java} | 6 +- ...tSuite.java => LiveTestSuiteLiveTest.java} | 2 +- 9 files changed, 154 insertions(+), 107 deletions(-) create mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java create mode 100644 spring-rest-full/.attach_pid28499 rename spring-rest-full/src/test/java/org/baeldung/{TestSuite.java => TestSuiteLiveTest.java} (68%) rename spring-rest-full/src/test/java/org/baeldung/web/{LiveTestSuite.java => LiveTestSuiteLiveTest.java} (87%) diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java new file mode 100644 index 0000000000..fe0465156d --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java @@ -0,0 +1,86 @@ +package com.baeldung.web.error; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import com.baeldung.web.exception.MyDataAccessException; +import com.baeldung.web.exception.MyDataIntegrityViolationException; +import com.baeldung.web.exception.MyResourceNotFoundException; + +@ControllerAdvice +public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + public RestResponseEntityExceptionHandler() { + super(); + } + + // API + + // 400 + /* + * Some examples of exceptions that we could retrieve as 400 (BAD_REQUEST) responses: + * Hibernate's ConstraintViolationException + * Spring's DataIntegrityViolationException + */ + @ExceptionHandler({ MyDataIntegrityViolationException.class }) + public ResponseEntity handleBadRequest(final MyDataIntegrityViolationException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); + } + + @Override + protected ResponseEntity handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + // ex.getCause() instanceof JsonMappingException, JsonParseException // for additional information later on + return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); + } + + @Override + protected ResponseEntity handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); + } + + + // 404 + /* + * Some examples of exceptions that we could retrieve as 404 (NOT_FOUND) responses: + * Java Persistence's EntityNotFoundException + */ + @ExceptionHandler(value = { MyResourceNotFoundException.class }) + protected ResponseEntity handleNotFound(final RuntimeException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request); + } + + // 409 + /* + * Some examples of exceptions that we could retrieve as 409 (CONFLICT) responses: + * Spring's InvalidDataAccessApiUsageException + * Spring's DataAccessException + */ + @ExceptionHandler({ MyDataAccessException.class}) + protected ResponseEntity handleConflict(final RuntimeException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); + } + + // 412 + + // 500 + + @ExceptionHandler({ NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class }) + /*500*/public ResponseEntity handleInternal(final RuntimeException ex, final WebRequest request) { + logger.error("500 Status Code", ex); + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java new file mode 100644 index 0000000000..8fc9a3a0ea --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java @@ -0,0 +1,21 @@ +package com.baeldung.web.exception; + +public final class MyDataAccessException extends RuntimeException { + + public MyDataAccessException() { + super(); + } + + public MyDataAccessException(final String message, final Throwable cause) { + super(message, cause); + } + + public MyDataAccessException(final String message) { + super(message); + } + + public MyDataAccessException(final Throwable cause) { + super(cause); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java new file mode 100644 index 0000000000..50adb09c09 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java @@ -0,0 +1,21 @@ +package com.baeldung.web.exception; + +public final class MyDataIntegrityViolationException extends RuntimeException { + + public MyDataIntegrityViolationException() { + super(); + } + + public MyDataIntegrityViolationException(final String message, final Throwable cause) { + super(message, cause); + } + + public MyDataIntegrityViolationException(final String message) { + super(message); + } + + public MyDataIntegrityViolationException(final Throwable cause) { + super(cause); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java new file mode 100644 index 0000000000..fd002efc28 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyResourceNotFoundException.java @@ -0,0 +1,21 @@ +package com.baeldung.web.exception; + +public final class MyResourceNotFoundException extends RuntimeException { + + public MyResourceNotFoundException() { + super(); + } + + public MyResourceNotFoundException(final String message, final Throwable cause) { + super(message, cause); + } + + public MyResourceNotFoundException(final String message) { + super(message); + } + + public MyResourceNotFoundException(final Throwable cause) { + super(cause); + } + +} diff --git a/spring-rest-full/.attach_pid28499 b/spring-rest-full/.attach_pid28499 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-rest-full/pom.xml b/spring-rest-full/pom.xml index 81c938a289..ddc7e042b5 100644 --- a/spring-rest-full/pom.xml +++ b/spring-rest-full/pom.xml @@ -212,23 +212,6 @@ org.apache.maven.plugins maven-war-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - - 3 - true - - **/*IntegrationTest.java - **/*IntTest.java - **/*LongRunningUnitTest.java - **/*ManualTest.java - **/*LiveTest.java - **/*TestSuite.java - - - org.codehaus.cargo cargo-maven2-plugin @@ -274,32 +257,6 @@ live - - org.apache.maven.plugins - maven-surefire-plugin - - - integration-test - - test - - - - **/*IntegrationTest.java - **/*IntTest.java - - - **/*LiveTest.java - - - - - - - json - - - org.codehaus.cargo cargo-maven2-plugin diff --git a/spring-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java b/spring-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java index b593116c4a..c0639acef4 100644 --- a/spring-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java +++ b/spring-rest-full/src/main/java/org/baeldung/web/error/RestResponseEntityExceptionHandler.java @@ -1,17 +1,9 @@ package org.baeldung.web.error; -import javax.persistence.EntityNotFoundException; - import org.baeldung.web.exception.MyResourceNotFoundException; -import org.hibernate.exception.ConstraintViolationException; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; @@ -24,61 +16,10 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH super(); } - // API - - // 400 - - @ExceptionHandler({ ConstraintViolationException.class }) - public ResponseEntity handleBadRequest(final ConstraintViolationException ex, final WebRequest request) { - final String bodyOfResponse = "This should be application specific"; - return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); - } - - @ExceptionHandler({ DataIntegrityViolationException.class }) - public ResponseEntity handleBadRequest(final DataIntegrityViolationException ex, final WebRequest request) { - final String bodyOfResponse = "This should be application specific"; - return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); - } - - @Override - protected ResponseEntity handleHttpMessageNotReadable(final HttpMessageNotReadableException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { - final String bodyOfResponse = "This should be application specific"; - // ex.getCause() instanceof JsonMappingException, JsonParseException // for additional information later on - return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); - } - - @Override - protected ResponseEntity handleMethodArgumentNotValid(final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { - final String bodyOfResponse = "This should be application specific"; - return handleExceptionInternal(ex, bodyOfResponse, headers, HttpStatus.BAD_REQUEST, request); - } - - - // 404 - - @ExceptionHandler(value = { EntityNotFoundException.class, MyResourceNotFoundException.class }) + @ExceptionHandler(value = { MyResourceNotFoundException.class }) protected ResponseEntity handleNotFound(final RuntimeException ex, final WebRequest request) { final String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request); } - // 409 - - @ExceptionHandler({ InvalidDataAccessApiUsageException.class, DataAccessException.class }) - protected ResponseEntity handleConflict(final RuntimeException ex, final WebRequest request) { - final String bodyOfResponse = "This should be application specific"; - return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); - } - - // 412 - - // 500 - - @ExceptionHandler({ NullPointerException.class, IllegalArgumentException.class, IllegalStateException.class }) - /*500*/public ResponseEntity handleInternal(final RuntimeException ex, final WebRequest request) { - logger.error("500 Status Code", ex); - final String bodyOfResponse = "This should be application specific"; - return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); - } - } diff --git a/spring-rest-full/src/test/java/org/baeldung/TestSuite.java b/spring-rest-full/src/test/java/org/baeldung/TestSuiteLiveTest.java similarity index 68% rename from spring-rest-full/src/test/java/org/baeldung/TestSuite.java rename to spring-rest-full/src/test/java/org/baeldung/TestSuiteLiveTest.java index cd5fa4661f..76215bb6e3 100644 --- a/spring-rest-full/src/test/java/org/baeldung/TestSuite.java +++ b/spring-rest-full/src/test/java/org/baeldung/TestSuiteLiveTest.java @@ -1,7 +1,7 @@ package org.baeldung; import org.baeldung.persistence.PersistenceTestSuite; -import org.baeldung.web.LiveTestSuite; +import org.baeldung.web.LiveTestSuiteLiveTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -9,8 +9,8 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ // @formatter:off PersistenceTestSuite.class - ,LiveTestSuite.class + ,LiveTestSuiteLiveTest.class }) // -public class TestSuite { +public class TestSuiteLiveTest { } diff --git a/spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java b/spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuiteLiveTest.java similarity index 87% rename from spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java rename to spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuiteLiveTest.java index 6d5b94a686..71a61ed338 100644 --- a/spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuite.java +++ b/spring-rest-full/src/test/java/org/baeldung/web/LiveTestSuiteLiveTest.java @@ -10,6 +10,6 @@ import org.junit.runners.Suite; ,FooLiveTest.class ,FooPageableLiveTest.class }) // -public class LiveTestSuite { +public class LiveTestSuiteLiveTest { } From 684ee6040e4fad45a30da1d7d46b86ce49913b34 Mon Sep 17 00:00:00 2001 From: Ger Roza Date: Thu, 17 Jan 2019 17:44:49 -0200 Subject: [PATCH 2/4] Added example for spring boot exception handling --- spring-boot-rest/README.md | 1 + spring-boot-rest/pom.xml | 11 ++++ .../boot/ErrorHandlingBootApplication.java | 15 +++++ .../MyCustomErrorAttributes.java | 23 +++++++ .../configurations/MyErrorController.java | 31 +++++++++ .../boot/web/FaultyRestController.java | 15 +++++ .../web/SpringBootRestApplication.java | 2 +- .../errorhandling-application.properties | 3 + .../boot/ErrorHandlingLiveTest.java | 65 +++++++++++++++++++ .../web/SpringContextIntegrationTest.java | 2 +- spring-rest-full/README.md | 1 - 11 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java create mode 100644 spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java create mode 100644 spring-boot-rest/src/main/resources/errorhandling-application.properties create mode 100644 spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java diff --git a/spring-boot-rest/README.md b/spring-boot-rest/README.md index 0a8d13cf76..2b955ddc5b 100644 --- a/spring-boot-rest/README.md +++ b/spring-boot-rest/README.md @@ -1,3 +1,4 @@ Module for the articles that are part of the Spring REST E-book: 1. [Bootstrap a Web Application with Spring 5](https://www.baeldung.com/bootstraping-a-web-application-with-spring-and-java-based-configuration) +2. [Error Handling for REST with Spring](http://www.baeldung.com/exception-handling-for-rest-with-spring) diff --git a/spring-boot-rest/pom.xml b/spring-boot-rest/pom.xml index baf9d35a09..3b8cf7e39e 100644 --- a/spring-boot-rest/pom.xml +++ b/spring-boot-rest/pom.xml @@ -20,12 +20,22 @@ org.springframework.boot spring-boot-starter-web + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + org.springframework.boot spring-boot-starter-test test + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + @@ -39,5 +49,6 @@ com.baeldung.SpringBootRestApplication + 2.32 diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java new file mode 100644 index 0000000000..0885ecbbf7 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java @@ -0,0 +1,15 @@ +package com.baeldung.errorhandling.boot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication +@PropertySource("errorhandling-application.properties") +public class ErrorHandlingBootApplication { + + public static void main(String[] args) { + SpringApplication.run(ErrorHandlingBootApplication.class, args); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java new file mode 100644 index 0000000000..e2c62cb907 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java @@ -0,0 +1,23 @@ +package com.baeldung.errorhandling.boot.configurations; + +import java.util.Map; + +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.WebRequest; + +@Component +public class MyCustomErrorAttributes extends DefaultErrorAttributes { + + @Override + public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { + Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); + errorAttributes.put("locale", webRequest.getLocale() + .toString()); + errorAttributes.remove("error"); + errorAttributes.put("cause", errorAttributes.get("message")); + errorAttributes.remove("message"); + errorAttributes.put("status", String.valueOf(errorAttributes.get("status"))); + return errorAttributes; + } +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java new file mode 100644 index 0000000000..427a0b43d7 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java @@ -0,0 +1,31 @@ +package com.baeldung.errorhandling.boot.configurations; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.boot.autoconfigure.web.ErrorProperties; +import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; +import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; + +@Component +public class MyErrorController extends BasicErrorController { + + public MyErrorController(ErrorAttributes errorAttributes) { + super(errorAttributes, new ErrorProperties()); + } + + @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE) + public ResponseEntity> xmlError(HttpServletRequest request) { + Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.APPLICATION_XML)); + body.put("xmlkey", "the XML response is different!"); + HttpStatus status = getStatus(request); + return new ResponseEntity<>(body, status); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java new file mode 100644 index 0000000000..e56e395754 --- /dev/null +++ b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java @@ -0,0 +1,15 @@ +package com.baeldung.errorhandling.boot.web; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FaultyRestController { + + @GetMapping("/exception") + public ResponseEntity requestWithException() { + throw new NullPointerException("Error in the faulty controller!"); + } + +} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/SpringBootRestApplication.java b/spring-boot-rest/src/main/java/com/baeldung/web/SpringBootRestApplication.java index 62aae7619d..c945b20aa1 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/SpringBootRestApplication.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/SpringBootRestApplication.java @@ -1,4 +1,4 @@ -package com.baeldung; +package com.baeldung.web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/spring-boot-rest/src/main/resources/errorhandling-application.properties b/spring-boot-rest/src/main/resources/errorhandling-application.properties new file mode 100644 index 0000000000..994c517163 --- /dev/null +++ b/spring-boot-rest/src/main/resources/errorhandling-application.properties @@ -0,0 +1,3 @@ + +#server.error.whitelabel.enabled=false +#server.error.include-stacktrace=always \ No newline at end of file diff --git a/spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java new file mode 100644 index 0000000000..e587b67fcf --- /dev/null +++ b/spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java @@ -0,0 +1,65 @@ +package com.baeldung.errorhandling.boot; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.isA; +import static org.hamcrest.Matchers.not; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +public class ErrorHandlingLiveTest { + + private static final String BASE_URL = "http://localhost:8080"; + private static final String EXCEPTION_ENDPOINT = "/exception"; + + private static final String ERROR_RESPONSE_KEY_PATH = "error"; + private static final String XML_RESPONSE_KEY_PATH = "xmlkey"; + private static final String LOCALE_RESPONSE_KEY_PATH = "locale"; + private static final String CAUSE_RESPONSE_KEY_PATH = "cause"; + private static final String RESPONSE_XML_ROOT = "Map"; + private static final String XML_RESPONSE_KEY_XML_PATH = RESPONSE_XML_ROOT + "." + XML_RESPONSE_KEY_PATH; + private static final String LOCALE_RESPONSE_KEY_XML_PATH = RESPONSE_XML_ROOT + "." + LOCALE_RESPONSE_KEY_PATH; + private static final String CAUSE_RESPONSE_KEY_XML_PATH = RESPONSE_XML_ROOT + "." + CAUSE_RESPONSE_KEY_PATH; + private static final String CAUSE_RESPONSE_VALUE = "Error in the faulty controller!"; + private static final String XML_RESPONSE_VALUE = "the XML response is different!"; + + @Test + public void whenRequestingFaultyEndpointAsJson_thenReceiveDefaultResponseWithConfiguredAttrs() { + given().header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) + .get(EXCEPTION_ENDPOINT) + .then() + .body("$", hasKey(LOCALE_RESPONSE_KEY_PATH)) + .body(CAUSE_RESPONSE_KEY_PATH, is(CAUSE_RESPONSE_VALUE)) + .body("$", not(hasKey(ERROR_RESPONSE_KEY_PATH))) + .body("$", not(hasKey(XML_RESPONSE_KEY_PATH))); + } + + @Test + public void whenRequestingFaultyEndpointAsXml_thenReceiveXmlResponseWithConfiguredAttrs() { + given().header(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML_VALUE) + .get(EXCEPTION_ENDPOINT) + .then() + .body(LOCALE_RESPONSE_KEY_XML_PATH, isA(String.class)) + .body(CAUSE_RESPONSE_KEY_XML_PATH, is(CAUSE_RESPONSE_VALUE)) + .body(RESPONSE_XML_ROOT, not(hasKey(ERROR_RESPONSE_KEY_PATH))) + .body(XML_RESPONSE_KEY_XML_PATH, is(XML_RESPONSE_VALUE)); + } + + @Test + public void whenRequestingFaultyEndpointAsHtml_thenReceiveWhitelabelPageResponse() throws Exception { + try (WebClient webClient = new WebClient()) { + webClient.getOptions() + .setThrowExceptionOnFailingStatusCode(false); + HtmlPage page = webClient.getPage(BASE_URL + EXCEPTION_ENDPOINT); + assertThat(page.getBody() + .asText()).contains("Whitelabel Error Page"); + } + } +} diff --git a/spring-boot-rest/src/test/java/com/baeldung/web/SpringContextIntegrationTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/SpringContextIntegrationTest.java index 0c1fdf372b..1e49df2909 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/web/SpringContextIntegrationTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/SpringContextIntegrationTest.java @@ -1,4 +1,4 @@ -package com.baeldung.spring.boot.rest; +package com.baeldung.web; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/spring-rest-full/README.md b/spring-rest-full/README.md index d429e17671..3a8d0a727a 100644 --- a/spring-rest-full/README.md +++ b/spring-rest-full/README.md @@ -18,7 +18,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com - [Metrics for your Spring REST API](http://www.baeldung.com/spring-rest-api-metrics) - [Bootstrap a Web Application with Spring 4](http://www.baeldung.com/bootstraping-a-web-application-with-spring-and-java-based-configuration) - [Build a REST API with Spring and Java Config](http://www.baeldung.com/building-a-restful-web-service-with-spring-and-java-based-configuration) -- [Error Handling for REST with Spring](http://www.baeldung.com/exception-handling-for-rest-with-spring) - [Spring Security Expressions - hasRole Example](https://www.baeldung.com/spring-security-expressions-basic) From 8a307c1aba5b8d186f893e68273733ce67cbd882 Mon Sep 17 00:00:00 2001 From: Ger Roza Date: Sat, 19 Jan 2019 09:32:54 -0200 Subject: [PATCH 3/4] moved new package to existing one --- .../boot/ErrorHandlingBootApplication.java | 15 --------------- .../config}/MyCustomErrorAttributes.java | 2 +- .../config}/MyErrorController.java | 2 +- .../controller}/FaultyRestController.java | 4 ++-- .../src/main/resources/application.properties | 3 +++ .../errorhandling-application.properties | 3 --- .../boot => web/error}/ErrorHandlingLiveTest.java | 2 +- 7 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java rename spring-boot-rest/src/main/java/com/baeldung/{errorhandling/boot/configurations => web/config}/MyCustomErrorAttributes.java (93%) rename spring-boot-rest/src/main/java/com/baeldung/{errorhandling/boot/configurations => web/config}/MyErrorController.java (95%) rename spring-boot-rest/src/main/java/com/baeldung/{errorhandling/boot/web => web/controller}/FaultyRestController.java (73%) delete mode 100644 spring-boot-rest/src/main/resources/errorhandling-application.properties rename spring-boot-rest/src/test/java/com/baeldung/{errorhandling/boot => web/error}/ErrorHandlingLiveTest.java (98%) diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java b/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java deleted file mode 100644 index 0885ecbbf7..0000000000 --- a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/ErrorHandlingBootApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.baeldung.errorhandling.boot; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.PropertySource; - -@SpringBootApplication -@PropertySource("errorhandling-application.properties") -public class ErrorHandlingBootApplication { - - public static void main(String[] args) { - SpringApplication.run(ErrorHandlingBootApplication.class, args); - } - -} diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java similarity index 93% rename from spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java rename to spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java index e2c62cb907..1948d5552f 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyCustomErrorAttributes.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyCustomErrorAttributes.java @@ -1,4 +1,4 @@ -package com.baeldung.errorhandling.boot.configurations; +package com.baeldung.web.config; import java.util.Map; diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java similarity index 95% rename from spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java rename to spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java index 427a0b43d7..e3716ec113 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/configurations/MyErrorController.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/config/MyErrorController.java @@ -1,4 +1,4 @@ -package com.baeldung.errorhandling.boot.configurations; +package com.baeldung.web.config; import java.util.Map; diff --git a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FaultyRestController.java similarity index 73% rename from spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java rename to spring-boot-rest/src/main/java/com/baeldung/web/controller/FaultyRestController.java index e56e395754..bf7b7a5f99 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/errorhandling/boot/web/FaultyRestController.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/controller/FaultyRestController.java @@ -1,4 +1,4 @@ -package com.baeldung.errorhandling.boot.web; +package com.baeldung.web.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -9,7 +9,7 @@ public class FaultyRestController { @GetMapping("/exception") public ResponseEntity requestWithException() { - throw new NullPointerException("Error in the faulty controller!"); + throw new RuntimeException("Error in the faulty controller!"); } } diff --git a/spring-boot-rest/src/main/resources/application.properties b/spring-boot-rest/src/main/resources/application.properties index e69de29bb2..e65440e2b9 100644 --- a/spring-boot-rest/src/main/resources/application.properties +++ b/spring-boot-rest/src/main/resources/application.properties @@ -0,0 +1,3 @@ +### Spring Boot default error handling configurations +#server.error.whitelabel.enabled=false +#server.error.include-stacktrace=always \ No newline at end of file diff --git a/spring-boot-rest/src/main/resources/errorhandling-application.properties b/spring-boot-rest/src/main/resources/errorhandling-application.properties deleted file mode 100644 index 994c517163..0000000000 --- a/spring-boot-rest/src/main/resources/errorhandling-application.properties +++ /dev/null @@ -1,3 +0,0 @@ - -#server.error.whitelabel.enabled=false -#server.error.include-stacktrace=always \ No newline at end of file diff --git a/spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java b/spring-boot-rest/src/test/java/com/baeldung/web/error/ErrorHandlingLiveTest.java similarity index 98% rename from spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java rename to spring-boot-rest/src/test/java/com/baeldung/web/error/ErrorHandlingLiveTest.java index e587b67fcf..ea1b6ab227 100644 --- a/spring-boot-rest/src/test/java/com/baeldung/errorhandling/boot/ErrorHandlingLiveTest.java +++ b/spring-boot-rest/src/test/java/com/baeldung/web/error/ErrorHandlingLiveTest.java @@ -1,4 +1,4 @@ -package com.baeldung.errorhandling.boot; +package com.baeldung.web.error; import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; From dea88da77728edb77c12475ca551045ac2960503 Mon Sep 17 00:00:00 2001 From: Ger Roza Date: Sat, 19 Jan 2019 17:00:30 -0200 Subject: [PATCH 4/4] Adding dao dependencies and exception handling of original classes --- spring-boot-rest/pom.xml | 8 ++++ .../RestResponseEntityExceptionHandler.java | 41 +++++++++---------- .../web/exception/MyDataAccessException.java | 21 ---------- .../MyDataIntegrityViolationException.java | 21 ---------- 4 files changed, 28 insertions(+), 63 deletions(-) delete mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java delete mode 100644 spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java diff --git a/spring-boot-rest/pom.xml b/spring-boot-rest/pom.xml index 3b8cf7e39e..f05d242072 100644 --- a/spring-boot-rest/pom.xml +++ b/spring-boot-rest/pom.xml @@ -24,6 +24,14 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml + + org.hibernate + hibernate-entitymanager + + + org.springframework + spring-jdbc + org.springframework.boot diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java index fe0465156d..2e2672f510 100644 --- a/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java +++ b/spring-boot-rest/src/main/java/com/baeldung/web/error/RestResponseEntityExceptionHandler.java @@ -1,5 +1,11 @@ package com.baeldung.web.error; +import javax.persistence.EntityNotFoundException; + +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -10,8 +16,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; -import com.baeldung.web.exception.MyDataAccessException; -import com.baeldung.web.exception.MyDataIntegrityViolationException; import com.baeldung.web.exception.MyResourceNotFoundException; @ControllerAdvice @@ -24,13 +28,15 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH // API // 400 - /* - * Some examples of exceptions that we could retrieve as 400 (BAD_REQUEST) responses: - * Hibernate's ConstraintViolationException - * Spring's DataIntegrityViolationException - */ - @ExceptionHandler({ MyDataIntegrityViolationException.class }) - public ResponseEntity handleBadRequest(final MyDataIntegrityViolationException ex, final WebRequest request) { + + @ExceptionHandler({ ConstraintViolationException.class }) + public ResponseEntity handleBadRequest(final ConstraintViolationException ex, final WebRequest request) { + final String bodyOfResponse = "This should be application specific"; + return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); + } + + @ExceptionHandler({ DataIntegrityViolationException.class }) + public ResponseEntity handleBadRequest(final DataIntegrityViolationException ex, final WebRequest request) { final String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.BAD_REQUEST, request); } @@ -50,23 +56,16 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH // 404 - /* - * Some examples of exceptions that we could retrieve as 404 (NOT_FOUND) responses: - * Java Persistence's EntityNotFoundException - */ - @ExceptionHandler(value = { MyResourceNotFoundException.class }) + + @ExceptionHandler(value = { EntityNotFoundException.class, MyResourceNotFoundException.class }) protected ResponseEntity handleNotFound(final RuntimeException ex, final WebRequest request) { final String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.NOT_FOUND, request); } // 409 - /* - * Some examples of exceptions that we could retrieve as 409 (CONFLICT) responses: - * Spring's InvalidDataAccessApiUsageException - * Spring's DataAccessException - */ - @ExceptionHandler({ MyDataAccessException.class}) + + @ExceptionHandler({ InvalidDataAccessApiUsageException.class, DataAccessException.class }) protected ResponseEntity handleConflict(final RuntimeException ex, final WebRequest request) { final String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); @@ -83,4 +82,4 @@ public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionH return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request); } -} +} \ No newline at end of file diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java deleted file mode 100644 index 8fc9a3a0ea..0000000000 --- a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataAccessException.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.baeldung.web.exception; - -public final class MyDataAccessException extends RuntimeException { - - public MyDataAccessException() { - super(); - } - - public MyDataAccessException(final String message, final Throwable cause) { - super(message, cause); - } - - public MyDataAccessException(final String message) { - super(message); - } - - public MyDataAccessException(final Throwable cause) { - super(cause); - } - -} diff --git a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java b/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java deleted file mode 100644 index 50adb09c09..0000000000 --- a/spring-boot-rest/src/main/java/com/baeldung/web/exception/MyDataIntegrityViolationException.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.baeldung.web.exception; - -public final class MyDataIntegrityViolationException extends RuntimeException { - - public MyDataIntegrityViolationException() { - super(); - } - - public MyDataIntegrityViolationException(final String message, final Throwable cause) { - super(message, cause); - } - - public MyDataIntegrityViolationException(final String message) { - super(message); - } - - public MyDataIntegrityViolationException(final Throwable cause) { - super(cause); - } - -}