[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-2</module>
|
||||
<module>spring-reactive-3</module>
|
||||
<module>spring-reactive-4</module>
|
||||
<module>spring-reactive-client</module>
|
||||
<module>spring-reactive-client-2</module>
|
||||
<module>spring-reactive-filters</module>
|
||||
|
|
|
@ -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…
Reference in New Issue