[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.
This commit is contained in:
parent
f18a5c4472
commit
9219e0216e
@ -20,6 +20,7 @@
|
|||||||
<module>spring-reactive-data</module>
|
<module>spring-reactive-data</module>
|
||||||
<module>spring-reactive-2</module>
|
<module>spring-reactive-2</module>
|
||||||
<module>spring-reactive-3</module>
|
<module>spring-reactive-3</module>
|
||||||
|
<module>spring-reactive-4</module>
|
||||||
<module>spring-reactive-client</module>
|
<module>spring-reactive-client</module>
|
||||||
<module>spring-reactive-client-2</module>
|
<module>spring-reactive-client-2</module>
|
||||||
<module>spring-reactive-filters</module>
|
<module>spring-reactive-filters</module>
|
||||||
|
53
spring-reactive-modules/spring-reactive-4/pom.xml
Normal file
53
spring-reactive-modules/spring-reactive-4/pom.xml
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>spring-reactive-4</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>spring-reactive-4</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<description>spring sample project about new features</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-boot-3</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../parent-boot-3</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<wiremock-standalone.version>3.4.2</wiremock-standalone.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wiremock</groupId>
|
||||||
|
<artifactId>wiremock-standalone</artifactId>
|
||||||
|
<version>${wiremock-standalone.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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<String> trace(@RequestHeader(name = "traceId") final String traceId) {
|
||||||
|
return Mono.just("TraceId: ".concat(traceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/trace-exceptional")
|
||||||
|
public Mono<String> traceExceptional() {
|
||||||
|
return Mono.just("Traced");
|
||||||
|
}
|
||||||
|
}
|
@ -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<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
if (exchange.getRequest()
|
||||||
|
.getPath()
|
||||||
|
.toString()
|
||||||
|
.equals("/trace-exceptional")) {
|
||||||
|
exchange.getRequest()
|
||||||
|
.getHeaders()
|
||||||
|
.add("traceId", "TRACE-ID");
|
||||||
|
}
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
}
|
@ -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<ServerResponse, ServerResponse> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> handlerFunction) {
|
||||||
|
|
||||||
|
ServerRequest serverRequest = ServerRequest.from(request)
|
||||||
|
.header("traceId", "FUNCTIONAL-TRACE-ID")
|
||||||
|
.build();
|
||||||
|
return handlerFunction.handle(serverRequest);
|
||||||
|
}
|
||||||
|
}
|
@ -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<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||||
|
exchange.getRequest()
|
||||||
|
.mutate()
|
||||||
|
.header("traceId", "ANNOTATED-TRACE-ID");
|
||||||
|
return chain.filter(exchange);
|
||||||
|
}
|
||||||
|
}
|
@ -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<String, String> changedMap) {
|
||||||
|
return (request, next) -> {
|
||||||
|
final ClientRequest clientRequest = ClientRequest.from(request)
|
||||||
|
.header("traceId", "TRACE-ID")
|
||||||
|
.build();
|
||||||
|
changedMap.addAll(clientRequest.headers());
|
||||||
|
return next.exchange(clientRequest);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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<ServerResponse> handle(final ServerRequest serverRequest) {
|
||||||
|
|
||||||
|
final String traceId = serverRequest.headers()
|
||||||
|
.firstHeader("traceId");
|
||||||
|
assert traceId != null;
|
||||||
|
final Mono<String> body = Mono.just("TraceId: ".concat(traceId));
|
||||||
|
return ok().body(body, String.class);
|
||||||
|
}
|
||||||
|
}
|
@ -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<ServerResponse> 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());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
</configuration>
|
@ -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<String> 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<Map> result = webTestClient.get()
|
||||||
|
.uri("/trace-exceptional")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.is5xxServerError()
|
||||||
|
.expectBody(Map.class)
|
||||||
|
.returnResult();
|
||||||
|
|
||||||
|
assertNotNull(result.getResponseBody());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.baeldung.filters;
|
||||||
|
|
||||||
|
import static com.baeldung.filters.WebClientFilters.modifyRequestHeaders;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.get;
|
||||||
|
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
|
||||||
|
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||||
|
|
||||||
|
@WireMockTest
|
||||||
|
public class WebClientFilterUnitTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static WireMockExtension extension = WireMockExtension.newInstance()
|
||||||
|
.options(wireMockConfig().dynamicPort()
|
||||||
|
.dynamicHttpsPort())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCallEndpoint_thenRequestHeadersModified() {
|
||||||
|
extension.stubFor(get("/test").willReturn(aResponse().withStatus(200)
|
||||||
|
.withBody("SUCCESS")));
|
||||||
|
|
||||||
|
final MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
||||||
|
|
||||||
|
WebClient webClient = WebClient.builder()
|
||||||
|
.filter(modifyRequestHeaders(map))
|
||||||
|
.build();
|
||||||
|
String actual = sendGetRequest(webClient);
|
||||||
|
|
||||||
|
final String body = "SUCCESS";
|
||||||
|
Assertions.assertEquals(actual, body);
|
||||||
|
Assertions.assertEquals("TRACE-ID", map.getFirst("traceId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String sendGetRequest(final WebClient webClient) {
|
||||||
|
return webClient.get()
|
||||||
|
.uri(getUrl())
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(String.class)
|
||||||
|
.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUrl() {
|
||||||
|
return "http://localhost:" + extension.getPort() + "/test";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package com.baeldung.handler;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
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.context.ApplicationContext;
|
||||||
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
import org.springframework.test.web.reactive.server.EntityExchangeResult;
|
||||||
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
|
|
||||||
|
import com.baeldung.router.TraceRouter;
|
||||||
|
|
||||||
|
@ExtendWith(SpringExtension.class)
|
||||||
|
@ContextConfiguration(classes = { TraceRouter.class, TraceRouterHandler.class })
|
||||||
|
@WebFluxTest
|
||||||
|
public class TraceRouteHandlerIntegrationTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext context;
|
||||||
|
private WebTestClient webTestClient;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
webTestClient = WebTestClient.bindToApplicationContext(context)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCallTraceFunctionalFilterEndpoint_thenResponseContainsTraceId() {
|
||||||
|
EntityExchangeResult<String> result = webTestClient.get()
|
||||||
|
.uri("/trace-functional-filter")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.returnResult();
|
||||||
|
|
||||||
|
final String body = "TraceId: FUNCTIONAL-TRACE-ID";
|
||||||
|
assertEquals(result.getResponseBody(), body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenCallTraceFunctionalBeforeEndpoint_thenResponseContainsTraceId() {
|
||||||
|
EntityExchangeResult<String> result = webTestClient.get()
|
||||||
|
.uri("/trace-functional-before")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBody(String.class)
|
||||||
|
.returnResult();
|
||||||
|
|
||||||
|
final String body = "TraceId: FUNCTIONAL-TRACE-ID";
|
||||||
|
assertEquals(result.getResponseBody(), body);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user