From 63e55f273099b93931e86c2773b1c4257e410055 Mon Sep 17 00:00:00 2001 From: Tonnix Date: Mon, 9 Jul 2018 13:23:56 +0300 Subject: [PATCH 1/2] Added source files for BAEL-1867 --- .../errorhandling/GlobalErrorAttributes.java | 55 +++++++++++++++++++ .../GlobalErrorWebExceptionHandler.java | 47 ++++++++++++++++ .../errorhandling/NameRequiredException.java | 12 ++++ .../errorhandling/handlers/Handler1.java | 29 ++++++++++ .../errorhandling/handlers/Handler2.java | 38 +++++++++++++ .../errorhandling/handlers/Handler3.java | 34 ++++++++++++ .../errorhandling/handlers/Handler4.java | 30 ++++++++++ .../errorhandling/handlers/Handler5.java | 22 ++++++++ .../errorhandling/routers/Router1.java | 22 ++++++++ .../errorhandling/routers/Router2.java | 22 ++++++++ .../errorhandling/routers/Router3.java | 22 ++++++++ .../errorhandling/routers/Router4.java | 22 ++++++++ .../errorhandling/routers/Router5.java | 22 ++++++++ 13 files changed, 377 insertions(+) create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java new file mode 100644 index 000000000000..a50651ced741 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorAttributes.java @@ -0,0 +1,55 @@ + +package com.baeldung.reactive.errorhandling; + +import java.util.Map; +import org.springframework.boot.web.reactive.error.DefaultErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; + +@Component +public class GlobalErrorAttributes extends DefaultErrorAttributes{ + + private HttpStatus status = HttpStatus.BAD_REQUEST; + private String message = "please provide a name"; + + public GlobalErrorAttributes() { + super(false); + } + + @Override + public Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) { + Map map = super.getErrorAttributes(request, includeStackTrace); + map.put("status", getStatus()); + map.put("message", getMessage()); + return map; + } + + /** + * @return the status + */ + public HttpStatus getStatus() { + return status; + } + + /** + * @param status the status to set + */ + public void setStatus(HttpStatus status) { + this.status = status; + } + + /** + * @return the message + */ + public String getMessage() { + return message; + } + + /** + * @param message the message to set + */ + public void setMessage(String message) { + this.message = message; + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java new file mode 100644 index 000000000000..09bccb0d5e41 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/GlobalErrorWebExceptionHandler.java @@ -0,0 +1,47 @@ + +package com.baeldung.reactive.errorhandling; + +import java.util.Map; +import org.springframework.boot.autoconfigure.web.ResourceProperties; +import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler; +import org.springframework.boot.web.reactive.error.ErrorAttributes; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +@Order(-2) +public class GlobalErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler { + + public GlobalErrorWebExceptionHandler(GlobalErrorAttributes g, ApplicationContext applicationContext, + ServerCodecConfigurer serverCodecConfigurer) { + super(g, new ResourceProperties(), applicationContext); + super.setMessageWriters(serverCodecConfigurer.getWriters()); + super.setMessageReaders(serverCodecConfigurer.getReaders()); + } + + @Override + protected RouterFunction getRoutingFunction(final ErrorAttributes errorAttributes) { + return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse); + } + + private Mono renderErrorResponse(final ServerRequest request) { + + final Map errorPropertiesMap = getErrorAttributes(request, false); + + return ServerResponse.status(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .body(BodyInserters.fromObject(errorPropertiesMap)); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java new file mode 100644 index 000000000000..38d35544a743 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/NameRequiredException.java @@ -0,0 +1,12 @@ + +package com.baeldung.reactive.errorhandling; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +public class NameRequiredException extends ResponseStatusException { + + public NameRequiredException(HttpStatus status, String message, Throwable e) { + super(status, message, e); + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java new file mode 100644 index 000000000000..87b78a4654b3 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler1.java @@ -0,0 +1,29 @@ + +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler1 { + + public Mono handleRequest1(ServerRequest request) { + return sayHello(request).onErrorReturn("Hello, Stranger") + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .syncBody(s)); + } + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java new file mode 100644 index 000000000000..12172a0f5435 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler2.java @@ -0,0 +1,38 @@ + +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler2 { + +public Mono handleRequest2(ServerRequest request) { + return + sayHello(request) + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .syncBody(s)) + .onErrorResume(e -> sayHelloFallback() + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .syncBody(s))); +} + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + + private Mono sayHelloFallback() { + return Mono.just("Hello, Stranger"); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java new file mode 100644 index 000000000000..e95b039ccee0 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler3.java @@ -0,0 +1,34 @@ + +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler3 { + + public Mono handleRequest3(ServerRequest request) { + return + sayHello(request) + .flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .syncBody(s)) + .onErrorResume(e -> (Mono.just("Hi, I looked around for your name but found: " + + e.getMessage())).flatMap(s -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .syncBody(s))); + } + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name") + .get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java new file mode 100644 index 000000000000..3d6ef258d322 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler4.java @@ -0,0 +1,30 @@ + +package com.baeldung.reactive.errorhandling.handlers; + +import com.baeldung.reactive.errorhandling.NameRequiredException; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler4 { + +public Mono handleRequest4(ServerRequest request) { + return ServerResponse.ok() + .body(sayHello(request) + .onErrorResume(e -> + Mono.error(new NameRequiredException( + HttpStatus.BAD_REQUEST, "please provide a name", e))), String.class); +} + + private Mono sayHello(ServerRequest request) { + try { + return Mono.just("Hello, " + request.queryParam("name").get()); + } catch (Exception e) { + return Mono.error(e); + } + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java new file mode 100644 index 000000000000..41605b355bbc --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/handlers/Handler5.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.handlers; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +@Component +public class Handler5 { + + public Mono handleRequest5(ServerRequest request) { + return ServerResponse.ok() + .body(sayHello(request), String.class); + + } + + private Mono sayHello(ServerRequest request) { + return Mono.just("Hello, " + request.queryParam("name").get()); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java new file mode 100644 index 000000000000..91be24571c65 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router1.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler1; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router1 { + + @Bean + public RouterFunction routeRequest1(Handler1 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint1") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest1); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java new file mode 100644 index 000000000000..bc7831f49465 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router2.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler2; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router2 { + + @Bean + public RouterFunction routeRequest2(Handler2 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint2") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest2); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java new file mode 100644 index 000000000000..461e6fe9e724 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router3.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler3; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router3 { + + @Bean + public RouterFunction routeRequest3(Handler3 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint3") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest3); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java new file mode 100644 index 000000000000..9dccc6858faa --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router4.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler4; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router4 { + + @Bean + public RouterFunction routeRequest4(Handler4 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint4") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest4); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java new file mode 100644 index 000000000000..59fd587fc857 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/errorhandling/routers/Router5.java @@ -0,0 +1,22 @@ + +package com.baeldung.reactive.errorhandling.routers; + +import com.baeldung.reactive.errorhandling.handlers.Handler5; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class Router5 { + + @Bean + public RouterFunction routeRequest5(Handler5 handler) { + return RouterFunctions.route(RequestPredicates.GET("/api/endpoint5") + .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest5); + } + +} From 1e69e96e7b54474a88ee593e8c12d14e63d469a2 Mon Sep 17 00:00:00 2001 From: Tonnix Date: Mon, 9 Jul 2018 13:25:02 +0300 Subject: [PATCH 2/2] Added test file for BAEL-1867 --- .../errorhandling/ErrorHandlingTest.java | 179 ++++++++++++++++++ .../errorhandling/ErrorHandlingTest.java~ | 179 ++++++++++++++++++ 2 files changed, 358 insertions(+) create mode 100644 spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java create mode 100644 spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java~ diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java new file mode 100644 index 000000000000..b1be911419f3 --- /dev/null +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java @@ -0,0 +1,179 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.reactive.errorhandling; + +import java.io.IOException; +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +public class ErrorHandlingTest { + + @Autowired + private WebTestClient webTestClient; + + @Test + public void testLocalErrorHandlingUsingOnErrorReturn() throws IOException { + + System.out.println("Testing local error handling using onErrorReturn"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint1?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint1") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Stranger", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithFallback() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with fallback"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint2?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint2") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Stranger", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithDynamicFallbackValue() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with dynamic fallback value"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint3?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint3") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hi, I looked around for your name but found: No value present", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithCatchAndRethrow() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with catch and rethrow"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint4?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + webTestClient.get() + .uri("/api/endpoint4") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isBadRequest() + .expectHeader() + .contentType(MediaType.APPLICATION_JSON_UTF8) + .expectBody() + .jsonPath("$.message") + .isNotEmpty() + .jsonPath("$.message") + .isEqualTo("please provide a name"); + + } + + @Test + public void testGlobalErrorHandlingUsingErrorWebExceptionHandler() throws IOException { + + System.out.println("Testing local error handling using ErrorWebExceptionHandler"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint5?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + webTestClient.get() + .uri("/api/endpoint5") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isBadRequest() + .expectHeader() + .contentType(MediaType.APPLICATION_JSON_UTF8) + .expectBody() + .jsonPath("$.message") + .isNotEmpty() + .jsonPath("$.message") + .isEqualTo("please provide a name"); + + } + +} diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java~ b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java~ new file mode 100644 index 000000000000..46df4e75f576 --- /dev/null +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/errorhandling/ErrorHandlingTest.java~ @@ -0,0 +1,179 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.baeldung.reactive.errorhandling; + +import java.io.IOException; +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.reactive.server.WebTestClient; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) +public class ApplicationTest { + + @Autowired + private WebTestClient webTestClient; + + @Test + public void testLocalErrorHandlingUsingOnErrorReturn() throws IOException { + + System.out.println("Testing local error handling using onErrorReturn"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint1?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint1") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Stranger", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithFallback() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with fallback"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint2?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint2") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Stranger", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithDynamicFallbackValue() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with dynamic fallback value"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint3?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + i = webTestClient.get() + .uri("/api/endpoint3") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hi, I looked around for your name but found: No value present", i); + + } + + @Test + public void testLocalErrorHandlingUsingOnErrorResumeWithCatchAndRethrow() throws IOException { + + System.out.println("Testing local error handling using onErrorResume with catch and rethrow"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint4?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + webTestClient.get() + .uri("/api/endpoint4") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isBadRequest() + .expectHeader() + .contentType(MediaType.APPLICATION_JSON_UTF8) + .expectBody() + .jsonPath("$.message") + .isNotEmpty() + .jsonPath("$.message") + .isEqualTo("please provide a name"); + + } + + @Test + public void testGlobalErrorHandlingUsingErrorWebExceptionHandler() throws IOException { + + System.out.println("Testing local error handling using ErrorWebExceptionHandler"); + + // Pass a username + String i = webTestClient.get() + .uri("/api/endpoint5?name={username}", "Tony") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .returnResult(String.class) + .getResponseBody() + .blockFirst(); + + assertEquals("Hello, Tony", i); + + // Do not pass a username + webTestClient.get() + .uri("/api/endpoint5") + .accept(MediaType.TEXT_PLAIN) + .exchange() + .expectStatus() + .isBadRequest() + .expectHeader() + .contentType(MediaType.APPLICATION_JSON_UTF8) + .expectBody() + .jsonPath("$.message") + .isNotEmpty() + .jsonPath("$.message") + .isEqualTo("please provide a name"); + + } + +}