This commit is contained in:
Jonathan Cook
2019-10-23 15:01:44 +02:00
parent db85c8f275
commit 684ec0d2e3
20486 changed files with 1642483 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
#folders#
.idea
/target
/neoDb*
/data
/src/main/webapp/WEB-INF/classes
*/META-INF/*
# Packaged files #
*.jar
*.war
*.ear
+20
View File
@@ -0,0 +1,20 @@
## Spring 5 Reactive Project
This module contains articles about reactive Spring 5
### The Course
The "REST With Spring" Classes: https://bit.ly/restwithspring
### Relevant Articles
- [Introduction to the Functional Web Framework in Spring 5](https://www.baeldung.com/spring-5-functional-web)
- [Spring 5 WebClient](https://www.baeldung.com/spring-5-webclient)
- [Exploring the Spring 5 WebFlux URL Matching](https://www.baeldung.com/spring-5-mvc-url-matching)
- [Reactive WebSockets with Spring 5](https://www.baeldung.com/spring-5-reactive-websockets)
- [Spring Webflux Filters](https://www.baeldung.com/spring-webflux-filters)
- [How to Set a Header on a Response with Spring 5](https://www.baeldung.com/spring-response-header)
- [Spring Webflux and CORS](https://www.baeldung.com/spring-webflux-cors)
- [Handling Errors in Spring WebFlux](https://www.baeldung.com/spring-webflux-errors)
- [Server-Sent Events in Spring](https://www.baeldung.com/spring-server-sent-events)
- [A Guide to Spring Session Reactive Support: WebSession](https://www.baeldung.com/spring-session-reactive)
- More articles: [[next -->]](/spring-5-reactive-2)
+161
View File
@@ -0,0 +1,161 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>spring-5-reactive</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-5-reactive</name>
<packaging>jar</packaging>
<description>spring 5 sample project about new features</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-json_1.1_spec</artifactId>
<version>${geronimo-json_1.1_spec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
</dependency>
<!-- utils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- runtime and test scoped -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Spring WebFlux WebSession -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons-collections4.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/assets</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes/assets</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.baeldung.reactive.Spring5ReactiveApplication</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
<johnzon.version>1.1.3</johnzon.version>
<jsonb-api.version>1.0</jsonb-api.version>
<geronimo-json_1.1_spec.version>1.0</geronimo-json_1.1_spec.version>
<commons-collections4.version>4.1</commons-collections4.version>
</properties>
</project>
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Baeldung: Static Content in Spring WebFlux</title>
</head>
<body>
Example Spring Web Flux and web resources configuration
</body>
</html>
@@ -0,0 +1,23 @@
package com.baeldung.functional;
class Actor {
private String firstname;
private String lastname;
public Actor() {
}
public Actor(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
}
@@ -0,0 +1,40 @@
package com.baeldung.functional;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class FormHandler {
Mono<ServerResponse> handleLogin(ServerRequest request) {
return request.body(toFormData())
.map(MultiValueMap::toSingleValueMap)
.filter(formData -> "baeldung".equals(formData.get("user")))
.filter(formData -> "you_know_what_to_do".equals(formData.get("token")))
.flatMap(formData -> ok().body(Mono.just("welcome back!"), String.class))
.switchIfEmpty(ServerResponse.badRequest()
.build());
}
Mono<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {
AtomicLong atomicLong = new AtomicLong(0);
dataBuffers.forEach(d -> atomicLong.addAndGet(d.readableByteCount()));
return atomicLong;
}
}
@@ -0,0 +1,72 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.core.publisher.Flux;
@SpringBootApplication
@ComponentScan(basePackages = { "com.baeldung.functional" })
public class FunctionalSpringBootApplication {
private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
private static final List<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> restfulRouter = route(GET("/"),
serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"),
serverRequest -> serverRequest.bodyToMono(Actor.class)
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld")))
.andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(path("/actor"), restfulRouter)
.filter((request, next) -> {
System.out.println("Before handler invocation: " + request.path());
return next.handle(request);
});
}
@Bean
public ServletRegistrationBean servletRegistrationBean() throws Exception {
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler((WebHandler) toHttpHandler(routingFunction()))
.filter(new IndexRewriteFilter())
.build();
ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new RootServlet(httpHandler), "/");
registrationBean.setLoadOnStartup(1);
registrationBean.setAsyncSupported(true);
return registrationBean;
}
public static void main(String[] args) {
SpringApplication.run(FunctionalSpringBootApplication.class, args);
}
}
@@ -0,0 +1,82 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.core.publisher.Flux;
public class FunctionalWebApplication {
private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
private static final List<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(path("/actor"), restfulRouter)
.filter((request, next) -> {
System.out.println("Before handler invocation: " + request.path());
return next.handle(request);
});
}
WebServer start() throws Exception {
WebHandler webHandler = (WebHandler) toHttpHandler(routingFunction());
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
.filter(new IndexRewriteFilter())
.build();
Tomcat tomcat = new Tomcat();
tomcat.setHostname("localhost");
tomcat.setPort(9090);
Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
Wrapper servletWrapper = Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
servletWrapper.setAsyncSupported(true);
rootContext.addServletMappingDecoded("/", "httpHandlerServlet");
TomcatWebServer server = new TomcatWebServer(tomcat);
server.start();
return server;
}
public static void main(String[] args) {
try {
new FunctionalWebApplication().start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,25 @@
package com.baeldung.functional;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
class IndexRewriteFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ServerHttpRequest request = serverWebExchange.getRequest();
if (request.getURI()
.getPath()
.equals("/")) {
return webFilterChain.filter(serverWebExchange.mutate()
.request(builder -> builder.method(request.getMethod())
.path("/test"))
.build());
}
return webFilterChain.filter(serverWebExchange);
}
}
@@ -0,0 +1,82 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class RootServlet extends ServletHttpHandlerAdapter {
public RootServlet() {
this(WebHttpHandlerBuilder.webHandler((WebHandler) toHttpHandler(routingFunction()))
.filter(new IndexRewriteFilter())
.build());
}
RootServlet(HttpHandler httpHandler) {
super(httpHandler);
}
private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
private static final List<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private static RouterFunction<?> routingFunction() {
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), serverRequest -> serverRequest.body(toFormData())
.map(MultiValueMap::toSingleValueMap)
.map(formData -> {
System.out.println("form data: " + formData.toString());
if ("baeldung".equals(formData.get("user")) && "you_know_what_to_do".equals(formData.get("token"))) {
return ok().body(Mono.just("welcome back!"), String.class)
.block();
}
return ServerResponse.badRequest()
.build()
.block();
}))
.andRoute(POST("/upload"), serverRequest -> serverRequest.body(toDataBuffers())
.collectList()
.map(dataBuffers -> {
AtomicLong atomicLong = new AtomicLong(0);
dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
.array().length));
System.out.println("data length:" + atomicLong.get());
return ok().body(fromObject(atomicLong.toString()))
.block();
}))
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(path("/actor"), route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
.doOnNext(actors::add)
.then(ok().build())))
.filter((request, next) -> {
System.out.println("Before handler invocation: " + request.path());
return next.handle(request);
});
}
}
@@ -0,0 +1,13 @@
package com.baeldung.reactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Spring5ReactiveApplication{
public static void main(String[] args) {
SpringApplication.run(Spring5ReactiveApplication.class, args);
}
}
@@ -0,0 +1,46 @@
package com.baeldung.reactive.controller;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.time.Duration;
import java.util.Random;
import java.util.stream.Stream;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.baeldung.reactive.model.Foo;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@RestController
public class FooReactiveController {
@GetMapping("/foos/{id}")
public Mono<Foo> getFoo(@PathVariable("id") long id) {
return Mono.just(new Foo(id, randomAlphabetic(6)));
}
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos")
public Flux<Foo> getAllFoos2() {
final Flux<Foo> foosFlux = Flux.fromStream(Stream.generate(() -> new Foo(new Random().nextLong(), randomAlphabetic(6))));
final Flux<Long> emmitFlux = Flux.interval(Duration.ofSeconds(1));
return Flux.zip(foosFlux, emmitFlux).map(Tuple2::getT1);
}
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE, value = "/foos2")
public Flux<Foo> getAllFoos() {
final Flux<Foo> flux = Flux.<Foo> create(fluxSink -> {
while (true) {
fluxSink.next(new Foo(new Random().nextLong(), randomAlphabetic(6)));
}
}).sample(Duration.ofSeconds(1)).log();
return flux;
}
}
@@ -0,0 +1,39 @@
package com.baeldung.reactive.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PathPatternController {
@GetMapping("/spring5/{*id}")
public String URIVariableHandler(@PathVariable String id) {
return id;
}
@GetMapping("/s?ring5")
public String wildcardTakingExactlyOneChar() {
return "/s?ring5";
}
@GetMapping("/spring5/*id")
public String wildcardTakingZeroOrMoreChar() {
return "/spring5/*id";
}
@GetMapping("/resources/**")
public String wildcardTakingZeroOrMorePathSegments() {
return "/resources/**";
}
@GetMapping("/{baeldung:[a-z]+}")
public String regexInPathVariable(@PathVariable String baeldung) {
return baeldung;
}
@GetMapping("/{var1}_{var2}")
public String multiplePathVariablesInSameSegment(@PathVariable String var1, @PathVariable String var2) {
return "Two variables are var1=" + var1 + " and var2=" + var2;
}
}
@@ -0,0 +1,25 @@
package com.baeldung.reactive.cors.annotated;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication
public class CorsOnAnnotatedElementsApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsOnAnnotatedElementsApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsAnnotatedSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,49 @@
package com.baeldung.reactive.cors.annotated.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@CrossOrigin(value = { "http://allowed-origin.com" }, allowedHeaders = { "Baeldung-Another-Allowed" }, maxAge = 900)
@RestController
@RequestMapping("/cors-on-controller")
public class CorsOnClassController {
@PutMapping("/regular-endpoint")
public Mono<String> corsDisabledEndpoint() {
return Mono.just("Regular endpoint");
}
@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono<String> corsEnabledEndpoint() {
return Mono.just("CORS enabled endpoint");
}
@CrossOrigin({ "http://another-allowed-origin.com" })
@PutMapping("/cors-enabled-origin-restrictive-endpoint")
public Mono<String> corsEnabledOriginRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Origin Restrictive");
}
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed" })
@PutMapping("/cors-enabled-header-restrictive-endpoint")
public Mono<String> corsEnabledHeaderRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Header Restrictive");
}
@CrossOrigin(exposedHeaders = { "Baeldung-Exposed" })
@PutMapping("/cors-enabled-exposed-header-endpoint")
public Mono<String> corsEnabledExposedHeadersEndpoint() {
return Mono.just("CORS enabled endpoint - Exposed Header");
}
@PutMapping("/cors-enabled-mixed-config-endpoint")
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed", "Baeldung-Other-Allowed" }, exposedHeaders = { "Baeldung-Allowed", "Baeldung-Exposed" }, maxAge = 3600)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS enabled endpoint - Mixed Config");
}
}
@@ -0,0 +1,48 @@
package com.baeldung.reactive.cors.annotated.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/cors-on-methods")
public class CorsOnMethodsController {
@PutMapping("/cors-disabled-endpoint")
public Mono<String> corsDisabledEndpoint() {
return Mono.just("CORS disabled endpoint");
}
@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono<String> corsEnabledEndpoint() {
return Mono.just("CORS enabled endpoint");
}
@CrossOrigin({ "http://allowed-origin.com" })
@PutMapping("/cors-enabled-origin-restrictive-endpoint")
public Mono<String> corsEnabledOriginRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Origin Restrictive");
}
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed" })
@PutMapping("/cors-enabled-header-restrictive-endpoint")
public Mono<String> corsEnabledHeaderRestrictiveEndpoint() {
return Mono.just("CORS enabled endpoint - Header Restrictive");
}
@CrossOrigin(exposedHeaders = { "Baeldung-Exposed" })
@PutMapping("/cors-enabled-exposed-header-endpoint")
public Mono<String> corsEnabledExposedHeadersEndpoint() {
return Mono.just("CORS enabled endpoint - Exposed Header");
}
@PutMapping("/cors-enabled-mixed-config-endpoint")
@CrossOrigin(allowedHeaders = { "Baeldung-Allowed", "Baeldung-Other-Allowed" }, exposedHeaders = { "Baeldung-Allowed", "Baeldung-Exposed" }, maxAge = 3600)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS enabled endpoint - Mixed Config");
}
}
@@ -0,0 +1,33 @@
package com.baeldung.reactive.cors.global;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class }
)
public class CorsGlobalConfigApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsGlobalConfigApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsGlobalSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.cors.global.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@Configuration
@EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
corsRegistry.addMapping("/**")
.allowedOrigins("http://allowed-origin.com")
.allowedMethods("PUT")
.allowedHeaders("Baeldung-Allowed", "Baledung-Another-Allowed")
.exposedHeaders("Baeldung-Allowed", "Baeldung-Exposed")
.maxAge(3600);
}
}
@@ -0,0 +1,24 @@
package com.baeldung.reactive.cors.global.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController("FurtherCorsConfigsController-cors-on-global-config-and-more")
@RequestMapping("/cors-on-global-config-and-more")
public class FurtherCorsConfigsController {
@DeleteMapping("/further-mixed-config-endpoint")
@CrossOrigin(methods = { RequestMethod.DELETE },
allowedHeaders = { "Baeldung-Other-Allowed" },
exposedHeaders = { "Baeldung-Other-Exposed" }
)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
return Mono.just("CORS Global Configured + Request Configs.");
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.cors.global.controllers;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController("RegularRestController-cors-on-global-config")
@RequestMapping("/cors-on-global-config")
public class RegularRestController {
@PutMapping("/regular-put-endpoint")
public Mono<String> regularPutEndpoint() {
return Mono.just("Regular PUT endpoint");
}
@DeleteMapping("/regular-delete-endpoint")
public Mono<String> regularDeleteEndpoint() {
return Mono.just("Regular DELETE endpoint");
}
}
@@ -0,0 +1,18 @@
package com.baeldung.reactive.cors.global.functional.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 CorsGlobalFunctionalHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
final String responseMessage = "CORS GLOBAL CONFIG IS NOT EFFECTIVE ON FUNCTIONAL ENDPOINTS!!!";
return ServerResponse.ok()
.body(Mono.just(responseMessage), String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.cors.global.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import com.baeldung.reactive.cors.global.functional.handlers.CorsGlobalFunctionalHandler;
@Configuration
public class CorsRouterFunctions {
@Bean
public RouterFunction<ServerResponse> corsGlobalRouter(@Autowired CorsGlobalFunctionalHandler handler) {
return RouterFunctions.route(RequestPredicates.PUT("/global-config-on-functional/cors-disabled-functional-endpoint"), handler::useHandler);
}
}
@@ -0,0 +1,34 @@
package com.baeldung.reactive.cors.webfilter;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoReactiveDataAutoConfiguration.class,
MongoReactiveAutoConfiguration.class }
)
public class CorsWebFilterApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(CorsWebFilterApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8083"));
app.run(args);
}
@Bean
public SecurityWebFilterChain corsWebfilterSpringSecurityFilterChain(ServerHttpSecurity http) {
http.csrf().disable();
return http.build();
}
}
@@ -0,0 +1,29 @@
package com.baeldung.reactive.cors.webfilter.config;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
@Configuration
public class CorsWebFilterConfig {
@Bean
CorsWebFilter corsWebFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowedOrigins(Arrays.asList("http://allowed-origin.com"));
corsConfig.setMaxAge(8000L);
corsConfig.addAllowedMethod("PUT");
corsConfig.addAllowedHeader("Baeldung-Allowed");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", corsConfig);
return new CorsWebFilter(source);
}
}
@@ -0,0 +1,26 @@
package com.baeldung.reactive.cors.webfilter.controllers;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
//@RestController
//@RequestMapping("/web-filter-and-more-on-annotated")
public class FurtherCorsConfigsController {
@DeleteMapping("/further-mixed-config-endpoint")
@CrossOrigin(methods = { RequestMethod.DELETE },
allowedHeaders = { "Baeldung-Other-Allowed" },
exposedHeaders = { "Baeldung-Other-Exposed" }
)
public Mono<String> corsEnabledHeaderExposedEndpoint() {
final String responseMessage = "CORS @CrossOrigin IS NOT EFFECTIVE with WebFilter!!!";
return Mono.just(responseMessage);
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.cors.webfilter.controllers;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
//@RestController
//@RequestMapping("/web-filter-on-annotated")
public class RegularRestController {
@PutMapping("/regular-put-endpoint")
public Mono<String> regularPutEndpoint() {
return Mono.just("Regular PUT endpoint");
}
@DeleteMapping("/regular-delete-endpoint")
public Mono<String> regularDeleteEndpoint() {
return Mono.just("Regular DELETE endpoint");
}
}
@@ -0,0 +1,16 @@
package com.baeldung.reactive.cors.webfilter.functional.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 CorsWithWebFilterHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
return ServerResponse.ok()
.body(Mono.just("Functional Endpoint"), String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.cors.webfilter.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import com.baeldung.reactive.cors.webfilter.functional.handlers.CorsWithWebFilterHandler;
@Configuration
public class CorsWithWebFilterRouterFunctions {
@Bean
public RouterFunction<ServerResponse> corsWebfilterRouter(@Autowired CorsWithWebFilterHandler handler) {
return RouterFunctions.route(RequestPredicates.PUT("/web-filter-on-functional/functional-endpoint"), handler::useHandler);
}
}
@@ -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<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
Map<String, Object> 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;
}
}
@@ -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<ServerResponse> getRoutingFunction(final ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(final ServerRequest request) {
final Map<String, Object> errorPropertiesMap = getErrorAttributes(request, false);
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(BodyInserters.fromObject(errorPropertiesMap));
}
}
@@ -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);
}
}
@@ -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<ServerResponse> handleRequest1(ServerRequest request) {
return sayHello(request).onErrorReturn("Hello, Stranger")
.flatMap(s -> ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.syncBody(s));
}
private Mono<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -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<ServerResponse> 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<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
private Mono<String> sayHelloFallback() {
return Mono.just("Hello, Stranger");
}
}
@@ -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<ServerResponse> 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<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name")
.get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -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<ServerResponse> 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<String> sayHello(ServerRequest request) {
try {
return Mono.just("Hello, " + request.queryParam("name").get());
} catch (Exception e) {
return Mono.error(e);
}
}
}
@@ -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<ServerResponse> handleRequest5(ServerRequest request) {
return ServerResponse.ok()
.body(sayHello(request), String.class);
}
private Mono<String> sayHello(ServerRequest request) {
return Mono.just("Hello, " + request.queryParam("name").get());
}
}
@@ -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<ServerResponse> routeRequest1(Handler1 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint1")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest1);
}
}
@@ -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<ServerResponse> routeRequest2(Handler2 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint2")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest2);
}
}
@@ -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<ServerResponse> routeRequest3(Handler3 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint3")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest3);
}
}
@@ -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<ServerResponse> routeRequest4(Handler4 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint4")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest4);
}
}
@@ -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<ServerResponse> routeRequest5(Handler5 handler) {
return RouterFunctions.route(RequestPredicates.GET("/api/endpoint5")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), handler::handleRequest5);
}
}
@@ -0,0 +1,22 @@
package com.baeldung.reactive.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;
import static org.springframework.http.HttpStatus.FORBIDDEN;
public class ExampleHandlerFilterFunction implements HandlerFilterFunction<ServerResponse, ServerResponse> {
@Override
public Mono<ServerResponse> filter(ServerRequest serverRequest, HandlerFunction<ServerResponse> handlerFunction) {
if (serverRequest.pathVariable("name").equalsIgnoreCase("test")) {
return ServerResponse.status(FORBIDDEN).build();
}
return handlerFunction.handle(serverRequest);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.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 ExampleWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
serverWebExchange.getResponse().getHeaders().add("web-filter", "web-filter-test");
return webFilterChain.filter(serverWebExchange);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.reactive.filters;
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
class PlayerHandler {
Mono<ServerResponse> getName(ServerRequest request) {
Mono<String> name = Mono.just(request.pathVariable("name"));
return ok().body(name, String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.filters;
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.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
@Configuration
public class PlayerRouter {
@Bean
public RouterFunction<ServerResponse> route(PlayerHandler playerHandler) {
return RouterFunctions
.route(GET("/players/{name}"), playerHandler::getName)
.filter(new ExampleHandlerFilterFunction());
}
}
@@ -0,0 +1,15 @@
package com.baeldung.reactive.filters;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class UserController {
@GetMapping(path = "/users/{name}")
public Mono<String> getName(@PathVariable String name) {
return Mono.just(name);
}
}
@@ -0,0 +1,13 @@
package com.baeldung.reactive.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class Foo {
private long id;
private String name;
}
@@ -0,0 +1,35 @@
package com.baeldung.reactive.responseheaders.controllers;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/response-header")
public class ResponseHeaderController {
@GetMapping("/response-entity")
public Mono<ResponseEntity<String>> usingResponseEntityBuilder() {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-ResponseEntityBuilder";
String responseBody = "Response with header using ResponseEntity (builder)";
return Mono.just(ResponseEntity.ok()
.header(responseHeaderKey, responseHeaderValue)
.body(responseBody));
}
@GetMapping("/server-http-response")
public Mono<String> usingServerHttpResponse(ServerHttpResponse response) {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-ServerHttpResponse";
String responseBody = "Response with header using ServerHttpResponse";
response.getHeaders().add(responseHeaderKey, responseHeaderValue);
return Mono.just(responseBody);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.responseheaders.filter;
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 AddResponseHeaderWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
exchange.getResponse()
.getHeaders()
.add("Baeldung-Example-Filter-Header", "Value-Filter");
return chain.filter(exchange);
}
}
@@ -0,0 +1,21 @@
package com.baeldung.reactive.responseheaders.functional.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 ResponseHeaderHandler {
public Mono<ServerResponse> useHandler(final ServerRequest request) {
String responseHeaderKey = "Baeldung-Example-Header";
String responseHeaderValue = "Value-Handler";
String responseBody = "Response with header using Handler";
return ServerResponse.ok()
.header(responseHeaderKey, responseHeaderValue)
.body(Mono.just(responseBody),String.class);
}
}
@@ -0,0 +1,20 @@
package com.baeldung.reactive.responseheaders.functional.routers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import com.baeldung.reactive.responseheaders.functional.handlers.ResponseHeaderHandler;
@Configuration
public class ResponseHeadersRouterFunctions {
@Bean
public RouterFunction<ServerResponse> responseHeaderRoute(@Autowired ResponseHeaderHandler handler) {
return RouterFunctions.route(RequestPredicates.GET("/functional-response-header/single-handler"), handler::useHandler);
}
}
@@ -0,0 +1,31 @@
package com.baeldung.reactive.serversentevents.consumer;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { RedisReactiveAutoConfiguration.class })
@EnableAsync
public class ConsumerSSEApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ConsumerSSEApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8082"));
app.run(args);
}
@Bean
public SecurityWebFilterChain sseConsumerSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange()
.permitAll();
return http.build();
}
}
@@ -0,0 +1,83 @@
package com.baeldung.reactive.serversentevents.consumer.controller;
import java.time.LocalTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/sse-consumer")
public class ClientController {
private static Logger logger = LoggerFactory.getLogger(ClientController.class);
private WebClient client = WebClient.create("http://localhost:8081/sse-server");
@GetMapping("/launch-sse-client")
public String launchSSEFromSSEWebClient() {
consumeSSE();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@GetMapping("/launch-flux-client")
public String launchcFluxFromSSEWebClient() {
consumeFlux();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@GetMapping("/launch-sse-from-flux-endpoint-client")
public String launchFluxFromFluxWebClient() {
consumeSSEFromFluxEndpoint();
return "LAUNCHED EVENT CLIENT!!! Check the logs...";
}
@Async
public void consumeSSE() {
ParameterizedTypeReference<ServerSentEvent<String>> type = new ParameterizedTypeReference<ServerSentEvent<String>>() {
};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(content -> logger.info("Current time: {} - Received SSE: name[{}], id [{}], content[{}] ", LocalTime.now(), content.event(), content.id(), content.data()), error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
@Async
public void consumeFlux() {
Flux<String> stringStream = client.get()
.uri("/stream-flux")
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(String.class);
stringStream.subscribe(content -> logger.info("Current time: {} - Received content: {} ", LocalTime.now(), content), error -> logger.error("Error retrieving content: {}", error), () -> logger.info("Completed!!!"));
}
@Async
public void consumeSSEFromFluxEndpoint() {
ParameterizedTypeReference<ServerSentEvent<String>> type = new ParameterizedTypeReference<ServerSentEvent<String>>() {
};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-flux")
.accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(content -> logger.info("Current time: {} - Received SSE: name[{}], id [{}], content[{}] ", LocalTime.now(), content.event(), content.id(), content.data()), error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}
}
@@ -0,0 +1,29 @@
package com.baeldung.reactive.serversentevents.server;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
@SpringBootApplication(exclude = { RedisReactiveAutoConfiguration.class })
public class ServerSSEApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ServerSSEApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8081"));
app.run(args);
}
@Bean
public SecurityWebFilterChain sseServerSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange()
.permitAll();
return http.build();
}
}
@@ -0,0 +1,35 @@
package com.baeldung.reactive.serversentevents.server.controllers;
import java.time.Duration;
import java.time.LocalTime;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/sse-server")
public class ServerController {
@GetMapping("/stream-sse")
public Flux<ServerSentEvent<String>> streamEvents() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> ServerSentEvent.<String> builder()
.id(String.valueOf(sequence))
.event("periodic-event")
.data("SSE - " + LocalTime.now()
.toString())
.build());
}
@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFlux() {
return Flux.interval(Duration.ofSeconds(1))
.map(sequence -> "Flux - " + LocalTime.now()
.toString());
}
}
@@ -0,0 +1,23 @@
package com.baeldung.reactive.urlmatch;
class Actor {
private String firstname;
private String lastname;
public Actor() {
}
public Actor(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
}
@@ -0,0 +1,63 @@
package com.baeldung.reactive.urlmatch;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
public class ExploreSpring5URLPatternUsingRouterFunctions {
private RouterFunction<ServerResponse> routingFunction() {
return route(GET("/p?ths"), serverRequest -> ok().body(fromObject("/p?ths"))).andRoute(GET("/test/{*id}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("id"))))
.andRoute(GET("/*card"), serverRequest -> ok().body(fromObject("/*card path was accessed")))
.andRoute(GET("/{var1}_{var2}"), serverRequest -> ok().body(fromObject(serverRequest.pathVariable("var1") + " , " + serverRequest.pathVariable("var2"))))
.andRoute(GET("/{baeldung:[a-z]+}"), serverRequest -> ok().body(fromObject("/{baeldung:[a-z]+} was accessed and baeldung=" + serverRequest.pathVariable("baeldung"))))
.and(RouterFunctions.resources("/files/{*filepaths}", new ClassPathResource("files/")));
}
WebServer start() throws Exception {
WebHandler webHandler = (WebHandler) toHttpHandler(routingFunction());
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
.filter(new IndexRewriteFilter())
.build();
Tomcat tomcat = new Tomcat();
tomcat.setHostname("localhost");
tomcat.setPort(9090);
Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
Wrapper servletWrapper = Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
servletWrapper.setAsyncSupported(true);
rootContext.addServletMappingDecoded("/", "httpHandlerServlet");
TomcatWebServer server = new TomcatWebServer(tomcat);
server.start();
return server;
}
public static void main(String[] args) {
try {
new FunctionalWebApplication().start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,41 @@
package com.baeldung.reactive.urlmatch;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import static org.springframework.web.reactive.function.BodyExtractors.toDataBuffers;
import static org.springframework.web.reactive.function.BodyExtractors.toFormData;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
public class FormHandler {
Mono<ServerResponse> handleLogin(ServerRequest request) {
return request.body(toFormData())
.map(MultiValueMap::toSingleValueMap)
.filter(formData -> "baeldung".equals(formData.get("user")))
.filter(formData -> "you_know_what_to_do".equals(formData.get("token")))
.flatMap(formData -> ok().body(Mono.just("welcome back!"), String.class))
.switchIfEmpty(ServerResponse.badRequest()
.build());
}
Mono<ServerResponse> handleUpload(ServerRequest request) {
return request.body(toDataBuffers())
.collectList()
.flatMap(dataBuffers -> ok().body(fromObject(extractData(dataBuffers).toString())));
}
private AtomicLong extractData(List<DataBuffer> dataBuffers) {
AtomicLong atomicLong = new AtomicLong(0);
dataBuffers.forEach(d -> atomicLong.addAndGet(d.asByteBuffer()
.array().length));
return atomicLong;
}
}
@@ -0,0 +1,80 @@
package com.baeldung.reactive.urlmatch;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.core.publisher.Flux;
public class FunctionalWebApplication {
private static final Actor BRAD_PITT = new Actor("Brad", "Pitt");
private static final Actor TOM_HANKS = new Actor("Tom", "Hanks");
private static final List<Actor> actors = new CopyOnWriteArrayList<>(Arrays.asList(BRAD_PITT, TOM_HANKS));
private RouterFunction<ServerResponse> routingFunction() {
FormHandler formHandler = new FormHandler();
RouterFunction<ServerResponse> restfulRouter = route(GET("/"), serverRequest -> ok().body(Flux.fromIterable(actors), Actor.class)).andRoute(POST("/"), serverRequest -> serverRequest.bodyToMono(Actor.class)
.doOnNext(actors::add)
.then(ok().build()));
return route(GET("/test"), serverRequest -> ok().body(fromObject("helloworld"))).andRoute(POST("/login"), formHandler::handleLogin)
.andRoute(POST("/upload"), formHandler::handleUpload)
.and(RouterFunctions.resources("/files/**", new ClassPathResource("files/")))
.andNest(path("/actor"), restfulRouter)
.filter((request, next) -> {
System.out.println("Before handler invocation: " + request.path());
return next.handle(request);
});
}
WebServer start() throws Exception {
WebHandler webHandler = (WebHandler) toHttpHandler(routingFunction());
HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(webHandler)
.filter(new IndexRewriteFilter())
.build();
Tomcat tomcat = new Tomcat();
tomcat.setHostname("localhost");
tomcat.setPort(9090);
Context rootContext = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
rootContext.addServletMappingDecoded("/", "httpHandlerServlet");
TomcatWebServer server = new TomcatWebServer(tomcat);
server.start();
return server;
}
public static void main(String[] args) {
try {
new FunctionalWebApplication().start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,27 @@
package com.baeldung.reactive.urlmatch;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
class IndexRewriteFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
ServerHttpRequest request = serverWebExchange.getRequest();
if (request.getURI()
.getPath()
.equals("/")) {
return webFilterChain.filter(serverWebExchange.mutate()
.request(builder -> builder.method(request.getMethod())
.contextPath(request.getPath()
.toString())
.path("/test"))
.build());
}
return webFilterChain.filter(serverWebExchange);
}
}
@@ -0,0 +1,15 @@
package com.baeldung.reactive.util;
import com.sun.management.OperatingSystemMXBean;
import java.lang.management.ManagementFactory;
public class CpuUtils {
private static OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
static Double getUsage() {
return (operatingSystemMXBean.getSystemCpuLoad() / operatingSystemMXBean.getAvailableProcessors()) * 100;
}
}
@@ -0,0 +1,28 @@
package com.baeldung.web.reactive;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Task {
private final String name;
private final int id;
public Task(@JsonProperty("name") String name, @JsonProperty("id") int id) {
this.name = name;
this.id = id;
}
public String getName() {
return this.name;
}
public int getId() {
return this.id;
}
@Override
public String toString() {
return "Task{" + "name='" + name + '\'' + ", id=" + id + '}';
}
}
@@ -0,0 +1,83 @@
package com.baeldung.web.reactive.client;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.nio.charset.Charset;
import java.time.ZonedDateTime;
import java.util.Collections;
@RestController
public class WebClientController {
@ResponseStatus(HttpStatus.OK)
@GetMapping("/resource")
public void getResource() {
}
public void demonstrateWebClient() {
// request
WebClient.UriSpec<WebClient.RequestBodySpec> request1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST);
WebClient.UriSpec<WebClient.RequestBodySpec> request2 = createWebClientWithServerURLAndDefaultValues().post();
// request body specifications
WebClient.RequestBodySpec uri1 = createWebClientWithServerURLAndDefaultValues().method(HttpMethod.POST)
.uri("/resource");
WebClient.RequestBodySpec uri2 = createWebClientWithServerURLAndDefaultValues().post()
.uri(URI.create("/resource"));
// request header specification
WebClient.RequestHeadersSpec<?> requestSpec1 = uri1.body(BodyInserters.fromPublisher(Mono.just("data"), String.class));
WebClient.RequestHeadersSpec<?> requestSpec2 = uri2.body(BodyInserters.fromObject("data"));
// inserters
BodyInserter<Publisher<String>, ReactiveHttpOutputMessage> inserter1 = BodyInserters
.fromPublisher(Subscriber::onComplete, String.class);
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("key1", "value1");
map.add("key2", "value2");
// BodyInserter<MultiValueMap<String, ?>, ClientHttpRequest> inserter2 = BodyInserters.fromMultipartData(map);
BodyInserter<String, ReactiveHttpOutputMessage> inserter3 = BodyInserters.fromObject("body");
// responses
WebClient.ResponseSpec response1 = uri1.body(inserter3)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
.acceptCharset(Charset.forName("UTF-8"))
.ifNoneMatch("*")
.ifModifiedSince(ZonedDateTime.now())
.retrieve();
WebClient.ResponseSpec response2 = requestSpec2.retrieve();
}
private WebClient createWebClient() {
return WebClient.create();
}
private WebClient createWebClientWithServerURL() {
return WebClient.create("http://localhost:8081");
}
private WebClient createWebClientWithServerURLAndDefaultValues() {
return WebClient.builder()
.baseUrl("http://localhost:8081")
.defaultCookie("cookieKey", "cookieValue")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultUriVariables(Collections.singletonMap("url", "http://localhost:8080"))
.build();
}
}
@@ -0,0 +1,15 @@
package com.baeldung.websession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.baeldung"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,17 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession;
@Configuration
//@EnableRedisWebSession
public class RedisConfig {
/**
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
*/
}
@@ -0,0 +1,20 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.ReactiveMapSessionRepository;
import org.springframework.session.ReactiveSessionRepository;
import org.springframework.session.config.annotation.web.server.EnableSpringWebSession;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@EnableSpringWebSession
public class SessionConfig {
@Bean
public ReactiveSessionRepository reactiveSessionRepository() {
return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
}
}
@@ -0,0 +1,20 @@
package com.baeldung.websession.configuration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@Configuration
@EnableWebFlux
public class WebFluxConfig implements ApplicationContextAware, WebFluxConfigurer {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
}
@@ -0,0 +1,56 @@
package com.baeldung.websession.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
@Configuration
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {
@Bean
public MapReactiveUserDetailsService userDetailsService() {
UserDetails admin = User
.withUsername("admin")
.password(encoder().encode("password"))
.roles("ADMIN")
.build();
UserDetails user = User
.withUsername("user")
.password(encoder().encode("password"))
.roles("USER")
.build();
return new MapReactiveUserDetailsService(admin, user);
}
@Bean
public SecurityWebFilterChain webSessionSpringSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.anyExchange().authenticated()
.and()
.httpBasic()
.securityContextRepository(new WebSessionServerSecurityContextRepository())
.and()
.formLogin();
http.csrf().disable();
return http.build();
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
@@ -0,0 +1,42 @@
package com.baeldung.websession.controller;
import com.baeldung.websession.transfer.CustomResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
@RestController
public class SessionController {
@GetMapping("/websession/test")
public Mono<CustomResponse> testWebSessionByParam(
@RequestParam(value = "id") int id,
@RequestParam(value = "note") String note,
WebSession session) {
session.getAttributes().put("id", id);
session.getAttributes().put("note", note);
CustomResponse r = new CustomResponse();
r.setId((int) session.getAttributes().get("id"));
r.setNote((String) session.getAttributes().get("note"));
return Mono.just(r);
}
@GetMapping("/websession")
public Mono<CustomResponse> getSession(WebSession session) {
session.getAttributes().putIfAbsent("id", 0);
session.getAttributes().putIfAbsent("note", "Howdy Cosmic Spheroid!");
CustomResponse r = new CustomResponse();
r.setId((int) session.getAttributes().get("id"));
r.setNote((String) session.getAttributes().get("note"));
return Mono.just(r);
}
}
@@ -0,0 +1,31 @@
package com.baeldung.websession.transfer;
public class CustomResponse {
private int id;
private String note;
public CustomResponse() {}
public CustomResponse(int id, String note) {
this.id = id;
this.note = note;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
@@ -0,0 +1,11 @@
package com.baeldung.websocket;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Event {
private String eventId;
private String eventDt;
}
@@ -0,0 +1,26 @@
package com.baeldung.websocket;
import java.net.URI;
import java.time.Duration;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import reactor.core.publisher.Mono;
public class ReactiveJavaClientWebSocket {
public static void main(String[] args) throws InterruptedException {
WebSocketClient client = new ReactorNettyWebSocketClient();
client.execute(
URI.create("ws://localhost:8080/event-emitter"),
session -> session.send(
Mono.just(session.textMessage("event-spring-reactive-client-websocket")))
.thenMany(session.receive()
.map(WebSocketMessage::getPayloadAsText)
.log())
.then())
.block(Duration.ofSeconds(10L));
}
}
@@ -0,0 +1,11 @@
package com.baeldung.websocket;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ReactiveWebSocketApplication {
public static void main(String[] args) {
SpringApplication.run(ReactiveWebSocketApplication.class, args);
}
}
@@ -0,0 +1,37 @@
package com.baeldung.websocket;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
@Configuration
public class ReactiveWebSocketConfiguration {
@Autowired
@Qualifier("ReactiveWebSocketHandler")
private WebSocketHandler webSocketHandler;
@Bean
public HandlerMapping webSocketHandlerMapping() {
Map<String, WebSocketHandler> map = new HashMap<>();
map.put("/event-emitter", webSocketHandler);
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(1);
handlerMapping.setUrlMap(map);
return handlerMapping;
}
@Bean
public WebSocketHandlerAdapter handlerAdapter() {
return new WebSocketHandlerAdapter();
}
}
@@ -0,0 +1,41 @@
package com.baeldung.websocket;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketMessage;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import static java.time.LocalDateTime.now;
import static java.util.UUID.randomUUID;
@Component("ReactiveWebSocketHandler")
public class ReactiveWebSocketHandler implements WebSocketHandler {
private static final ObjectMapper json = new ObjectMapper();
private Flux<String> eventFlux = Flux.generate(sink -> {
Event event = new Event(randomUUID().toString(), now().toString());
try {
sink.next(json.writeValueAsString(event));
} catch (JsonProcessingException e) {
sink.error(e);
}
});
private Flux<String> intervalFlux = Flux.interval(Duration.ofMillis(1000L))
.zipWith(eventFlux, (time, event) -> event);
@Override
public Mono<Void> handle(WebSocketSession webSocketSession) {
return webSocketSession.send(intervalFlux
.map(webSocketSession::textMessage))
.and(webSocketSession.receive()
.map(WebSocketMessage::getPayloadAsText).log());
}
}
@@ -0,0 +1 @@
logging.level.root=INFO
@@ -0,0 +1 @@
hello
@@ -0,0 +1 @@
test
@@ -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,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Baeldung: Spring 5 Reactive Client WebSocket (Browser)</title>
</head>
<body>
<div class="events"></div>
<script>
var clientWebSocket = new WebSocket("ws://localhost:8080/event-emitter");
clientWebSocket.onopen = function() {
console.log("clientWebSocket.onopen", clientWebSocket);
console.log("clientWebSocket.readyState", "websocketstatus");
clientWebSocket.send("event-me-from-browser");
}
clientWebSocket.onclose = function(error) {
console.log("clientWebSocket.onclose", clientWebSocket, error);
events("Closing connection");
}
clientWebSocket.onerror = function(error) {
console.log("clientWebSocket.onerror", clientWebSocket, error);
events("An error occured");
}
clientWebSocket.onmessage = function(error) {
console.log("clientWebSocket.onmessage", clientWebSocket, error);
events(error.data);
}
function events(responseEvent) {
document.querySelector(".events").innerHTML += responseEvent + "<br>";
}
</script>
</body>
</html>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Spring Functional Application</display-name>
<servlet>
<servlet-name>functional</servlet-name>
<servlet-class>com.baeldung.functional.RootServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>functional</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
@@ -0,0 +1,142 @@
package com.baeldung.functional;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromResource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.web.server.WebServer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
public class FunctionalWebApplicationIntegrationTest {
private static WebTestClient client;
private static WebServer server;
@BeforeClass
public static void setup() throws Exception {
server = new FunctionalWebApplication().start();
client = WebTestClient.bindToServer()
.baseUrl("http://localhost:" + server.getPort())
.build();
}
@AfterClass
public static void destroy() {
server.stop();
}
@Test
public void givenRouter_whenGetTest_thenGotHelloWorld() throws Exception {
client.get()
.uri("/test")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("helloworld");
}
@Test
public void givenIndexFilter_whenRequestRoot_thenRewrittenToTest() throws Exception {
client.get()
.uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("helloworld");
}
@Test
public void givenLoginForm_whenPostValidToken_thenSuccess() throws Exception {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(1);
formData.add("user", "baeldung");
formData.add("token", "you_know_what_to_do");
client.post()
.uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData))
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("welcome back!");
}
@Test
public void givenLoginForm_whenRequestWithInvalidToken_thenFail() throws Exception {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(2);
formData.add("user", "baeldung");
formData.add("token", "try_again");
client.post()
.uri("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData))
.exchange()
.expectStatus()
.isBadRequest();
}
@Test
public void givenUploadForm_whenRequestWithMultipartData_thenSuccess() throws Exception {
Resource resource = new ClassPathResource("/baeldung-weekly.png");
client.post()
.uri("/upload")
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(fromResource(resource))
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo(String.valueOf(resource.contentLength()));
}
@Test
public void givenActors_whenAddActor_thenAdded() throws Exception {
client.get()
.uri("/actor")
.exchange()
.expectStatus()
.isOk()
.expectBodyList(Actor.class)
.hasSize(2);
client.post()
.uri("/actor")
.body(fromObject(new Actor("Clint", "Eastwood")))
.exchange()
.expectStatus()
.isOk();
client.get()
.uri("/actor")
.exchange()
.expectStatus()
.isOk()
.expectBodyList(Actor.class)
.hasSize(3);
}
@Test
public void givenResources_whenAccess_thenGot() throws Exception {
client.get()
.uri("/files/hello.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("hello");
}
}
@@ -0,0 +1,32 @@
package com.baeldung.reactive;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.junit.Assert.assertNotNull;
import java.time.Duration;
import java.util.Random;
import org.junit.jupiter.api.Test;
import com.baeldung.reactive.model.Foo;
import reactor.core.publisher.Flux;
public class FluxUnitTest {
public static final Random RANDOM = new Random();
@Test
public void whenFluxIsConstructed_thenCorrect() {
final Flux<Foo> flux = Flux.<Foo> create(fluxSink -> {
for (int i = 0 ; i < 100 ; i++) {
fluxSink.next(new Foo(RANDOM.nextLong(), randomAlphabetic(6)));
}
}).sample(Duration.ofSeconds(1)).log();
flux.subscribe();
assertNotNull(flux);
}
}
@@ -0,0 +1,97 @@
package com.baeldung.reactive;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import java.time.Duration;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.baeldung.web.reactive.Task;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.DisposableServer;
import reactor.netty.http.server.HttpServer;
public class Spring5ReactiveServerClientIntegrationTest {
private static DisposableServer disposableServer;
@BeforeAll
public static void setUp() throws Exception {
HttpServer server = HttpServer.create()
.host("localhost")
.port(8080);
RouterFunction<?> route = RouterFunctions.route(POST("/task/process"), request -> ServerResponse.ok()
.body(request.bodyToFlux(Task.class)
.map(ll -> new Task("TaskName", 1)), Task.class))
.and(RouterFunctions.route(GET("/task"), request -> ServerResponse.ok()
.body(Mono.just("server is alive"), String.class)));
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
disposableServer = server.handle(adapter)
.bindNow();
}
@AfterAll
public static void shutDown() {
disposableServer.disposeNow();
}
// @Test
// public void givenCheckTask_whenServerHandle_thenServerResponseALiveString() throws Exception {
// WebClient client = WebClient.create("http://localhost:8080");
// Mono<String> result = client
// .get()
// .uri("/task")
// .exchange()
// .then(response -> response.bodyToMono(String.class));
//
// assertThat(result.block()).isInstanceOf(String.class);
// }
// @Test
// public void givenThreeTasks_whenServerHandleTheTasks_thenServerResponseATask() throws Exception {
// URI uri = URI.create("http://localhost:8080/task/process");
// ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
// ClientRequest request = ClientRequest
// .method(HttpMethod.POST, uri)
// .body(BodyInserters.fromPublisher(getLatLngs(), Task.class))
// .build();
//
// Flux<Task> taskResponse = exchange
// .exchange(request)
// .flatMap(response -> response.bodyToFlux(Task.class));
//
// assertThat(taskResponse.blockFirst()).isInstanceOf(Task.class);
// }
// @Test
// public void givenCheckTask_whenServerHandle_thenOragicServerResponseALiveString() throws Exception {
// URI uri = URI.create("http://localhost:8080/task");
// ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
// ClientRequest request = ClientRequest
// .method(HttpMethod.GET, uri)
// .body(BodyInserters.fromPublisher(getLatLngs(), Task.class))
// .build();
//
// Flux<String> taskResponse = exchange
// .exchange(request)
// .flatMap(response -> response.bodyToFlux(String.class));
//
// assertThat(taskResponse.blockFirst()).isInstanceOf(String.class);
// }
private static Flux<Task> getLatLngs() {
return Flux.range(0, 3)
.zipWith(Flux.interval(Duration.ofSeconds(1)))
.map(x -> new Task("taskname", 1))
.doOnNext(ll -> System.out.println("Produced: {}" + ll));
}
}
@@ -0,0 +1,144 @@
package com.baeldung.reactive.cors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CorsOnAnnotatedElementsLiveTest {
private static final String BASE_URL = "http://localhost:8081";
private static final String BASE_CORS_ON_METHODS_URL = "/cors-on-methods";
private static final String BASE_CORS_ON_CONTROLLER_URL = "/cors-on-controller";
private static final String CONTROLLER_CORS_ALLOWED_ORIGIN = "http://allowed-origin.com";
private static final String CORS_DEFAULT_ORIGIN = "http://default-origin.com";
private static WebTestClient client;
@BeforeAll
public static void setup() {
client = WebTestClient.bindToServer()
.baseUrl(BASE_URL)
.defaultHeader("Origin", CORS_DEFAULT_ORIGIN)
.build();
}
@Test
public void whenRequestingMethodCorsEnabledEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-enabled-endpoint")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", "*");
}
@Test
public void whenPreflightMethodCorsEnabled_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-enabled-endpoint")
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", "*");
response.expectHeader()
.valueEquals("Access-Control-Allow-Methods", "PUT");
response.expectHeader()
.exists("Access-Control-Max-Age");
}
@Test
public void whenRequestingMethodCorsDisabledEndpoint_thenObtainResponseWithoutCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-disabled-put-endpoint")
.exchange();
response.expectHeader()
.doesNotExist("Access-Control-Allow-Origin");
}
@Test
public void whenRequestingMethodCorsRestrictiveOrigin_thenObtainForbiddenResponse() {
ResponseSpec response = client.put()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-enabled-origin-restrictive-endpoint")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenPreflightMethodCorsRestrictiveOrigin_thenObtainForbiddenResponse() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-enabled-origin-restrictive-endpoint")
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenPreflightMethodCorsRestrictiveHeader_thenObtainResponseWithAllowedHeaders() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_METHODS_URL + "/cors-enabled-header-restrictive-endpoint")
.header("Access-Control-Request-Method", "PUT")
.header("Access-Control-Request-Headers", "Baeldung-Not-Allowed, Baeldung-Allowed")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Headers", "Baeldung-Allowed");
}
@Test
public void whenPreflightControllerCorsRegularEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_CONTROLLER_URL + "/regular-endpoint")
.header("Origin", CONTROLLER_CORS_ALLOWED_ORIGIN)
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CONTROLLER_CORS_ALLOWED_ORIGIN);
}
@Test
public void whenPreflightControllerCorsRestrictiveOrigin_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_CONTROLLER_URL + "/cors-enabled-origin-restrictive-endpoint")
.header("Origin", CONTROLLER_CORS_ALLOWED_ORIGIN)
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CONTROLLER_CORS_ALLOWED_ORIGIN);
}
@Test
public void whenPreflightControllerCorsRestrictiveOriginWithAnotherAllowedOrigin_thenObtainResponseWithCorsHeaders() {
final String anotherAllowedOrigin = "http://another-allowed-origin.com";
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_CONTROLLER_URL + "/cors-enabled-origin-restrictive-endpoint")
.header("Origin", anotherAllowedOrigin)
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", anotherAllowedOrigin);
}
@Test
public void whenPreflightControllerCorsExposingHeaders_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.options()
.uri(BASE_CORS_ON_CONTROLLER_URL + "/cors-enabled-exposed-header-endpoint")
.header("Origin", CONTROLLER_CORS_ALLOWED_ORIGIN)
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Expose-Headers", "Baeldung-Exposed");
}
}
@@ -0,0 +1,96 @@
package com.baeldung.reactive.cors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CorsOnGlobalConfigLiveTest {
private static final String BASE_URL = "http://localhost:8082";
private static final String BASE_REGULAR_URL = "/cors-on-global-config";
private static final String BASE_EXTRA_CORS_CONFIG_URL = "/cors-on-global-config-and-more";
private static final String BASE_FUNCTIONALS_URL = "/global-config-on-functional";
private static final String CORS_DEFAULT_ORIGIN = "http://allowed-origin.com";
private static WebTestClient client;
@BeforeAll
public static void setup() {
client = WebTestClient.bindToServer()
.baseUrl(BASE_URL)
.defaultHeader("Origin", CORS_DEFAULT_ORIGIN)
.build();
}
@Test
public void whenRequestingRegularEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_REGULAR_URL + "/regular-put-endpoint")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
}
@Test
public void whenRequestingRegularDeleteEndpoint_thenObtainForbiddenResponse() {
ResponseSpec response = client.delete()
.uri(BASE_REGULAR_URL + "/regular-delete-endpoint")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenPreflightAllowedDeleteEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.options()
.uri(BASE_EXTRA_CORS_CONFIG_URL + "/further-mixed-config-endpoint")
.header("Access-Control-Request-Method", "DELETE")
.header("Access-Control-Request-Headers", "Baeldung-Not-Allowed, Baeldung-Allowed, Baeldung-Other-Allowed")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
response.expectHeader()
.valueEquals("Access-Control-Allow-Methods", "PUT,DELETE");
response.expectHeader()
.valueEquals("Access-Control-Allow-Headers", "Baeldung-Allowed, Baeldung-Other-Allowed");
response.expectHeader()
.exists("Access-Control-Max-Age");
}
@Test
public void whenRequestAllowedDeleteEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.delete()
.uri(BASE_EXTRA_CORS_CONFIG_URL + "/further-mixed-config-endpoint")
.exchange();
response.expectStatus()
.isOk();
}
@Test
public void whenPreflightFunctionalEndpoint_thenObtain404Response() {
ResponseSpec response = client.options()
.uri(BASE_FUNCTIONALS_URL + "/cors-disabled-functional-endpoint")
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectStatus()
.isNotFound();
}
@Test
public void whenRequestFunctionalEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_FUNCTIONALS_URL + "/cors-disabled-functional-endpoint")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
}
}
@@ -0,0 +1,93 @@
package com.baeldung.reactive.cors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CorsOnWebFilterLiveTest {
private static final String BASE_URL = "http://localhost:8083";
private static final String BASE_REGULAR_URL = "/web-filter-on-annotated";
private static final String BASE_EXTRA_CORS_CONFIG_URL = "/web-filter-and-more-on-annotated";
private static final String BASE_FUNCTIONALS_URL = "/web-filter-on-functional";
private static final String CORS_DEFAULT_ORIGIN = "http://allowed-origin.com";
private static WebTestClient client;
@BeforeAll
public static void setup() {
client = WebTestClient.bindToServer()
.baseUrl(BASE_URL)
.defaultHeader("Origin", CORS_DEFAULT_ORIGIN)
.build();
}
@Test
public void whenRequestingRegularEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_REGULAR_URL + "/regular-put-endpoint")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
}
@Test
public void whenRequestingRegularDeleteEndpoint_thenObtainForbiddenResponse() {
ResponseSpec response = client.delete()
.uri(BASE_REGULAR_URL + "/regular-delete-endpoint")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenPreflightDeleteEndpointWithExtraConfigs_thenObtainForbiddenResponse() {
ResponseSpec response = client.options()
.uri(BASE_EXTRA_CORS_CONFIG_URL + "/further-mixed-config-endpoint")
.header("Access-Control-Request-Method", "DELETE")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenRequestDeleteEndpointWithExtraConfigs_thenObtainForbiddenResponse() {
ResponseSpec response = client.delete()
.uri(BASE_EXTRA_CORS_CONFIG_URL + "/further-mixed-config-endpoint")
.exchange();
response.expectStatus()
.isForbidden();
}
@Test
public void whenPreflightFunctionalEndpoint_thenObtain404Response() {
ResponseSpec response = client.options()
.uri(BASE_FUNCTIONALS_URL + "/functional-endpoint")
.header("Access-Control-Request-Method", "PUT")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
response.expectHeader()
.valueEquals("Access-Control-Allow-Methods", "PUT");
response.expectHeader()
.valueEquals("Access-Control-Max-Age", "8000");
}
@Test
public void whenRequestFunctionalEndpoint_thenObtainResponseWithCorsHeaders() {
ResponseSpec response = client.put()
.uri(BASE_FUNCTIONALS_URL + "/functional-endpoint")
.exchange();
response.expectHeader()
.valueEquals("Access-Control-Allow-Origin", CORS_DEFAULT_ORIGIN);
}
}
@@ -0,0 +1,176 @@
package com.baeldung.reactive.errorhandling;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext
@WithMockUser
@AutoConfigureWebTestClient(timeout = "10000")
public class ErrorHandlingIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void givenErrorReturn_whenUsernamePresent_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint1?name={username}", "Tony")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Tony", s);
}
@Test
public void givenErrorReturn_whenNoUsername_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint1")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Stranger", s);
}
@Test
public void givenResumeFallback_whenUsernamePresent_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint2?name={username}", "Tony")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Tony", s);
}
@Test
public void givenResumeFallback_whenNoUsername_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint2")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Stranger", s);
}
@Test
public void givenResumeDynamicValue_whenUsernamePresent_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint3?name={username}", "Tony")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Tony", s);
}
@Test
public void givenResumeDynamicValue_whenNoUsername_thenOk() throws IOException {
String s = 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", s);
}
@Test
public void givenResumeRethrow_whenUsernamePresent_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint4?name={username}", "Tony")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Tony", s);
}
@Test
public void givenResumeRethrow_whenNoUsername_thenOk() throws IOException {
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 givenGlobalErrorHandling_whenUsernamePresent_thenOk() throws IOException {
String s = webTestClient.get()
.uri("/api/endpoint5?name={username}", "Tony")
.accept(MediaType.TEXT_PLAIN)
.exchange()
.returnResult(String.class)
.getResponseBody()
.blockFirst();
assertEquals("Hello, Tony", s);
}
@Test
public void givenGlobalErrorHandling_whenNoUsername_thenOk() throws IOException {
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");
}
}
@@ -0,0 +1,43 @@
package com.baeldung.reactive.filters;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@WithMockUser
@AutoConfigureWebTestClient(timeout = "10000")
public class PlayerHandlerIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void whenPlayerNameIsBaeldung_thenWebFilterIsApplied() {
EntityExchangeResult<String> result = webTestClient.get().uri("/players/baeldung")
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.returnResult();
assertEquals(result.getResponseBody(), "baeldung");
assertEquals(result.getResponseHeaders().getFirst("web-filter"), "web-filter-test");
}
@Test
public void whenPlayerNameIsTest_thenHandlerFilterFunctionIsApplied() {
webTestClient.get().uri("/players/test")
.exchange()
.expectStatus().isForbidden();
}
}
@@ -0,0 +1,40 @@
package com.baeldung.reactive.filters;
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.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@WithMockUser
public class UserControllerIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Test
public void whenUserNameIsBaeldung_thenWebFilterIsApplied() {
EntityExchangeResult<String> result = webTestClient.get().uri("/users/baeldung")
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.returnResult();
assertEquals(result.getResponseBody(), "baeldung");
assertEquals(result.getResponseHeaders().getFirst("web-filter"), "web-filter-test");
}
@Test
public void whenUserNameIsTest_thenHandlerFilterFunctionIsNotApplied() {
webTestClient.get().uri("/users/test")
.exchange()
.expectStatus().isOk();
}
}
@@ -0,0 +1,60 @@
package com.baeldung.reactive.responseheaders;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.ResponseSpec;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ResponseHeaderLiveTest {
private static final String BASE_URL = "http://localhost:8080";
private static final String ANNOTATION_BASE_URL = BASE_URL + "/response-header";
private static final String FUNCTIONAL_BASE_URL = BASE_URL + "/functional-response-header";
private static final String SERVICE_SINGLE_RESPONSE_HEADER = "Baeldung-Example-Header";
private static final String SERVICE_FILTER_RESPONSE_HEADER = "Baeldung-Example-Filter-Header";
private static final String SERVICE_FILTER_RESPONSE_HEADER_VALUE = "Value-Filter";
private static WebTestClient client;
@BeforeAll
public static void setup() {
client = WebTestClient.bindToServer()
.baseUrl(BASE_URL)
.build();
}
@Test
public void whenUsingResponseEntityBuilderRequest_thenObtainResponseWithCorrectHeaders() {
client = WebTestClient.bindToServer()
.baseUrl(BASE_URL)
.build();
ResponseSpec response = client.get()
.uri(ANNOTATION_BASE_URL + "/response-entity")
.exchange();
response.expectHeader().valueEquals(SERVICE_SINGLE_RESPONSE_HEADER, "Value-ResponseEntityBuilder")
.expectHeader().valueEquals(SERVICE_FILTER_RESPONSE_HEADER, SERVICE_FILTER_RESPONSE_HEADER_VALUE);
}
@Test
public void whenUsingServerHttpResponseRequest_thenObtainResponseWithCorrectHeaders() {
ResponseSpec response = client.get()
.uri(ANNOTATION_BASE_URL + "/server-http-response")
.exchange();
response.expectHeader().valueEquals(SERVICE_SINGLE_RESPONSE_HEADER, "Value-ServerHttpResponse")
.expectHeader().valueEquals(SERVICE_FILTER_RESPONSE_HEADER, SERVICE_FILTER_RESPONSE_HEADER_VALUE);
}
@Test
public void whenUsingFunctionalHandlerRequest_thenObtainResponseWithCorrectHeaders() {
ResponseSpec response = client.get()
.uri(FUNCTIONAL_BASE_URL + "/single-handler")
.exchange();
response.expectHeader().valueEquals(SERVICE_SINGLE_RESPONSE_HEADER, "Value-Handler")
.expectHeader().valueEquals(SERVICE_FILTER_RESPONSE_HEADER, SERVICE_FILTER_RESPONSE_HEADER_VALUE);
}
}
@@ -0,0 +1,48 @@
package com.baeldung.reactive.serversentsevents;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@WithMockUser
public class ServiceSentEventLiveTest {
private WebTestClient client = WebTestClient.bindToServer()
.baseUrl("http://localhost:8081/sse-server")
.build();
@Test
public void whenSSEEndpointIsCalled_thenEventStreamingBegins() {
Executable sseStreamingCall = () -> client.get()
.uri("/stream-sse")
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM)
.expectBody(String.class);
Assertions.assertThrows(IllegalStateException.class, sseStreamingCall, "Expected test to timeout and throw IllegalStateException, but it didn't");
}
@Test
public void whenFluxEndpointIsCalled_thenEventStreamingBegins() {
Executable sseStreamingCall = () -> client.get()
.uri("/stream-flux")
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM)
.expectBody(String.class);
Assertions.assertThrows(IllegalStateException.class, sseStreamingCall, "Expected test to timeout and throw IllegalStateException, but it didn't");
}
}
@@ -0,0 +1,110 @@
package com.baeldung.reactive.urlmatch;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.boot.web.server.WebServer;
import org.springframework.test.web.reactive.server.WebTestClient;
public class ExploreSpring5URLPatternUsingRouterFunctionsIntegrationTest {
private static WebTestClient client;
private static WebServer server;
@BeforeClass
public static void setup() throws Exception {
server = new ExploreSpring5URLPatternUsingRouterFunctions().start();
client = WebTestClient.bindToServer()
.baseUrl("http://localhost:" + server.getPort())
.build();
}
@AfterClass
public static void destroy() {
server.stop();
}
@Test
public void givenRouter_whenGetPathWithSingleCharWildcard_thenGotPathPattern() throws Exception {
client.get()
.uri("/paths")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/p?ths");
}
@Test
public void givenRouter_whenMultipleURIVariablePattern_thenGotPathVariable() throws Exception {
client.get()
.uri("/test/ab/cd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/ab/cd");
}
@Test
public void givenRouter_whenGetMultipleCharWildcard_thenGotPathPattern() throws Exception {
client.get()
.uri("/wildcard")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/*card path was accessed");
}
@Test
public void givenRouter_whenGetMultiplePathVaribleInSameSegment_thenGotPathVariables() throws Exception {
client.get()
.uri("/baeldung_tutorial")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("baeldung , tutorial");
}
@Test
public void givenRouter_whenGetRegexInPathVarible_thenGotPathVariable() throws Exception {
client.get()
.uri("/abcd")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("/{baeldung:[a-z]+} was accessed and baeldung=abcd");
client.get()
.uri("/1234")
.exchange()
.expectStatus()
.is4xxClientError();
}
@Test
public void givenResources_whenAccess_thenGot() throws Exception {
client.get()
.uri("/files/test/test.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("test");
client.get()
.uri("/files/hello.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("hello");
}
}
@@ -0,0 +1,105 @@
package com.baeldung.reactive.urlmatch;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.baeldung.reactive.Spring5ReactiveApplication;
import com.baeldung.reactive.controller.PathPatternController;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveApplication.class)
@WithMockUser
public class PathPatternsUsingHandlerMethodIntegrationTest {
private static WebTestClient client;
@BeforeClass
public static void setUp() {
client = WebTestClient.bindToController(new PathPatternController())
.build();
}
@Test
public void givenHandlerMethod_whenMultipleURIVariablePattern_then200() {
client.get()
.uri("/spring5/ab/cd")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/ab/cd");
}
@Test
public void givenHandlerMethod_whenURLWithWildcardTakingZeroOrMoreChar_then200() {
client.get()
.uri("/spring5/userid")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/spring5/*id");
}
@Test
public void givenHandlerMethod_whenURLWithWildcardTakingExactlyOneChar_then200() {
client.get()
.uri("/string5")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/s?ring5");
}
@Test
public void givenHandlerMethod_whenURLWithWildcardTakingZeroOrMorePathSegments_then200() {
client.get()
.uri("/resources/baeldung")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("/resources/**");
}
@Test
public void givenHandlerMethod_whenURLWithRegexInPathVariable_thenExpectedOutput() {
client.get()
.uri("/abc")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("abc");
client.get()
.uri("/123")
.exchange()
.expectStatus()
.is4xxClientError();
}
@Test
public void givenHandlerMethod_whenURLWithMultiplePathVariablesInSameSegment_then200() {
client.get()
.uri("/baeldung_tutorial")
.exchange()
.expectStatus()
.is2xxSuccessful()
.expectBody()
.equals("Two variables are var1=baeldung and var2=tutorial");
}
}
@@ -0,0 +1,64 @@
package com.baeldung.web.client;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
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;
import org.springframework.web.server.WebHandler;
import com.baeldung.reactive.Spring5ReactiveApplication;
import reactor.core.publisher.Mono;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@WithMockUser
public class WebTestClientIntegrationTest {
@LocalServerPort
private int port;
private final RouterFunction ROUTER_FUNCTION = RouterFunctions.route(RequestPredicates.GET("/resource"), request -> ServerResponse.ok()
.build());
private final WebHandler WEB_HANDLER = exchange -> Mono.empty();
@Test
public void testWebTestClientWithServerWebHandler() {
WebTestClient.bindToWebHandler(WEB_HANDLER)
.build();
}
@Test
public void testWebTestClientWithRouterFunction() {
WebTestClient.bindToRouterFunction(ROUTER_FUNCTION)
.build()
.get()
.uri("/resource")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.isEmpty();
}
@Test
public void testWebTestClientWithServerURL() {
WebTestClient.bindToServer()
.baseUrl("http://localhost:" + port)
.build()
.get()
.uri("/resource")
.exchange()
.expectStatus()
.isOk()
.expectBody();
}
}
@@ -0,0 +1,17 @@
package org.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveApplication.class)
public class SpringContextIntegrationTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -0,0 +1,17 @@
package org.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.baeldung.reactive.Spring5ReactiveApplication;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5ReactiveApplication.class)
public class SpringContextTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB