From 9219e0216e0e7b2b24d1ada959f1c618fd1bee2f Mon Sep 17 00:00:00 2001
From: Ruchira Madhushan Rajapaksha
<52396694+LordMaduz@users.noreply.github.com>
Date: Sun, 14 Apr 2024 10:01:07 +0800
Subject: [PATCH] [BAEL-7546] - How to intercept a request and add headers in
WebFlux (#16291)
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Added the Implementation Code.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Added the JUnit Tests.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Updated parent module with the new module reference.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* CICD Build Fix for PMD Violation
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* CICD Build Fix for PMD Violation
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Use four space indents in the pom file.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Use 2 space indents line continuation.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Add test case for Unmodifiable Header map scenario.
* [BAEL-7546] - How to intercept a request and add headers in WebFlux
* Indentation fixes.
---
spring-reactive-modules/pom.xml | 1 +
.../spring-reactive-4/pom.xml | 53 ++++++++++++++++
.../baeldung/SpringReactiveApplication.java | 13 ++++
.../baeldung/controller/TraceController.java | 21 +++++++
.../filters/ExceptionalTraceFilter.java | 25 ++++++++
.../filters/TraceHandlerFilterFunction.java | 20 +++++++
.../com/baeldung/filters/TraceWebFilter.java | 20 +++++++
.../baeldung/filters/WebClientFilters.java | 18 ++++++
.../baeldung/handler/TraceRouterHandler.java | 22 +++++++
.../java/com/baeldung/router/TraceRouter.java | 27 +++++++++
.../src/main/resources/application.properties | 0
.../src/main/resources/logback.xml | 13 ++++
.../TraceControllerIntegrationTest.java | 50 ++++++++++++++++
.../filters/WebClientFilterUnitTest.java | 55 +++++++++++++++++
.../TraceRouteHandlerIntegrationTest.java | 60 +++++++++++++++++++
15 files changed, 398 insertions(+)
create mode 100644 spring-reactive-modules/spring-reactive-4/pom.xml
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/SpringReactiveApplication.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/controller/TraceController.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/ExceptionalTraceFilter.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceHandlerFilterFunction.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceWebFilter.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/WebClientFilters.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/handler/TraceRouterHandler.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/router/TraceRouter.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/resources/application.properties
create mode 100644 spring-reactive-modules/spring-reactive-4/src/main/resources/logback.xml
create mode 100644 spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/controller/TraceControllerIntegrationTest.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/filters/WebClientFilterUnitTest.java
create mode 100644 spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/handler/TraceRouteHandlerIntegrationTest.java
diff --git a/spring-reactive-modules/pom.xml b/spring-reactive-modules/pom.xml
index a8a9d6de8a..2618ea67aa 100644
--- a/spring-reactive-modules/pom.xml
+++ b/spring-reactive-modules/pom.xml
@@ -20,6 +20,7 @@
spring-reactive-data
spring-reactive-2
spring-reactive-3
+ spring-reactive-4
spring-reactive-client
spring-reactive-client-2
spring-reactive-filters
diff --git a/spring-reactive-modules/spring-reactive-4/pom.xml b/spring-reactive-modules/spring-reactive-4/pom.xml
new file mode 100644
index 0000000000..7fb099ec1e
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+ spring-reactive-4
+ 0.0.1-SNAPSHOT
+ spring-reactive-4
+ jar
+ spring sample project about new features
+
+
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
+
+
+
+ 3.4.2
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ io.projectreactor
+ reactor-test
+ test
+
+
+
+ io.netty
+ netty-all
+
+
+
+ org.wiremock
+ wiremock-standalone
+ ${wiremock-standalone.version}
+ test
+
+
+
+
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/SpringReactiveApplication.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/SpringReactiveApplication.java
new file mode 100644
index 0000000000..21fbfdac3e
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/SpringReactiveApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringReactiveApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringReactiveApplication.class, args);
+ }
+
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/controller/TraceController.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/controller/TraceController.java
new file mode 100644
index 0000000000..c6a564e6d6
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/controller/TraceController.java
@@ -0,0 +1,21 @@
+package com.baeldung.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RestController;
+
+import reactor.core.publisher.Mono;
+
+@RestController
+public class TraceController {
+
+ @GetMapping(value = "/trace-annotated")
+ public Mono trace(@RequestHeader(name = "traceId") final String traceId) {
+ return Mono.just("TraceId: ".concat(traceId));
+ }
+
+ @GetMapping(value = "/trace-exceptional")
+ public Mono traceExceptional() {
+ return Mono.just("Traced");
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/ExceptionalTraceFilter.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/ExceptionalTraceFilter.java
new file mode 100644
index 0000000000..b721d10109
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/ExceptionalTraceFilter.java
@@ -0,0 +1,25 @@
+package com.baeldung.filters;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+
+import reactor.core.publisher.Mono;
+
+@Component
+public class ExceptionalTraceFilter implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ if (exchange.getRequest()
+ .getPath()
+ .toString()
+ .equals("/trace-exceptional")) {
+ exchange.getRequest()
+ .getHeaders()
+ .add("traceId", "TRACE-ID");
+ }
+ return chain.filter(exchange);
+ }
+}
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceHandlerFilterFunction.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceHandlerFilterFunction.java
new file mode 100644
index 0000000000..708e4dc509
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceHandlerFilterFunction.java
@@ -0,0 +1,20 @@
+package com.baeldung.filters;
+
+import org.springframework.web.reactive.function.server.HandlerFilterFunction;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+
+import reactor.core.publisher.Mono;
+
+public class TraceHandlerFilterFunction implements HandlerFilterFunction {
+
+ @Override
+ public Mono filter(ServerRequest request, HandlerFunction handlerFunction) {
+
+ ServerRequest serverRequest = ServerRequest.from(request)
+ .header("traceId", "FUNCTIONAL-TRACE-ID")
+ .build();
+ return handlerFunction.handle(serverRequest);
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceWebFilter.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceWebFilter.java
new file mode 100644
index 0000000000..fcb5570db6
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/TraceWebFilter.java
@@ -0,0 +1,20 @@
+package com.baeldung.filters;
+
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+
+import reactor.core.publisher.Mono;
+
+@Component
+public class TraceWebFilter implements WebFilter {
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ exchange.getRequest()
+ .mutate()
+ .header("traceId", "ANNOTATED-TRACE-ID");
+ return chain.filter(exchange);
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/WebClientFilters.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/WebClientFilters.java
new file mode 100644
index 0000000000..1748d4d103
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/filters/WebClientFilters.java
@@ -0,0 +1,18 @@
+package com.baeldung.filters;
+
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.client.ClientRequest;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+
+public final class WebClientFilters {
+
+ public static ExchangeFilterFunction modifyRequestHeaders(final MultiValueMap changedMap) {
+ return (request, next) -> {
+ final ClientRequest clientRequest = ClientRequest.from(request)
+ .header("traceId", "TRACE-ID")
+ .build();
+ changedMap.addAll(clientRequest.headers());
+ return next.exchange(clientRequest);
+ };
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/handler/TraceRouterHandler.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/handler/TraceRouterHandler.java
new file mode 100644
index 0000000000..7285a564c3
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/handler/TraceRouterHandler.java
@@ -0,0 +1,22 @@
+package com.baeldung.handler;
+
+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;
+
+import static org.springframework.web.reactive.function.server.ServerResponse.ok;
+
+@Component
+public class TraceRouterHandler {
+
+ public Mono handle(final ServerRequest serverRequest) {
+
+ final String traceId = serverRequest.headers()
+ .firstHeader("traceId");
+ assert traceId != null;
+ final Mono body = Mono.just("TraceId: ".concat(traceId));
+ return ok().body(body, String.class);
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/router/TraceRouter.java b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/router/TraceRouter.java
new file mode 100644
index 0000000000..cd25477965
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/java/com/baeldung/router/TraceRouter.java
@@ -0,0 +1,27 @@
+package com.baeldung.router;
+
+import com.baeldung.filters.TraceHandlerFilterFunction;
+import com.baeldung.handler.TraceRouterHandler;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+
+import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
+import static org.springframework.web.reactive.function.server.RouterFunctions.route;
+
+@Configuration
+public class TraceRouter {
+
+ @Bean
+ public RouterFunction routes(TraceRouterHandler routerHandler) {
+ return route(GET("/trace-functional-filter"), routerHandler::handle).filter(new TraceHandlerFilterFunction())
+ .and(route().GET("/trace-functional-before", routerHandler::handle)
+ .before(request -> ServerRequest.from(request)
+ .header("traceId", "FUNCTIONAL-TRACE-ID")
+ .build())
+ .build());
+ }
+}
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/resources/application.properties b/spring-reactive-modules/spring-reactive-4/src/main/resources/application.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/spring-reactive-modules/spring-reactive-4/src/main/resources/logback.xml b/spring-reactive-modules/spring-reactive-4/src/main/resources/logback.xml
new file mode 100644
index 0000000000..26beb6d5b4
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/controller/TraceControllerIntegrationTest.java b/spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/controller/TraceControllerIntegrationTest.java
new file mode 100644
index 0000000000..eba9ba10a3
--- /dev/null
+++ b/spring-reactive-modules/spring-reactive-4/src/test/java/com/baeldung/controller/TraceControllerIntegrationTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.controller;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Map;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.reactive.server.EntityExchangeResult;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@ExtendWith(SpringExtension.class)
+@WebFluxTest(controllers = TraceController.class)
+public class TraceControllerIntegrationTest {
+
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Test
+ void whenCallTraceAnnotatedEndpoint_thenResponseContainsTraceId() {
+ EntityExchangeResult result = webTestClient.get()
+ .uri("/trace-annotated")
+ .exchange()
+ .expectStatus()
+ .isOk()
+ .expectBody(String.class)
+ .returnResult();
+
+ final String body = "TraceId: ANNOTATED-TRACE-ID";
+ assertEquals(result.getResponseBody(), body);
+
+ }
+
+ @Test
+ void whenCallTraceExceptionalEndpoint_thenThrowsException() {
+ EntityExchangeResult