Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions spring-5/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<!-- a fix for spring-boot dependency on Reactor -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.0.6.BUILD-SNAPSHOT</version>
</dependency>

<!-- utils -->
<dependency>
Expand Down
23 changes: 23 additions & 0 deletions spring-5/src/main/java/com/baeldung/functional/Actor.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
50 changes: 50 additions & 0 deletions spring-5/src/main/java/com/baeldung/functional/FormHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.baeldung.functional;

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.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)
.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();
});
}

Mono<ServerResponse> handleUpload(ServerRequest request) {
return request
.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();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.baeldung.functional;

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;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.*;
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;

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 = toHttpHandler(routingFunction());
HttpHandler httpHandler = WebHttpHandlerBuilder
.webHandler(webHandler)
.prependFilter(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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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())
.contextPath(request.getContextPath())
.path("/test"))
.build());
}
return webFilterChain.filter(serverWebExchange);
}

}
1 change: 1 addition & 0 deletions spring-5/src/main/resources/files/hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.baeldung.functional;

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.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;

import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.BodyInserters.fromResource;

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)
.value()
.isEqualTo("helloworld");
}

@Test
public void givenIndexFilter_whenRequestRoot_thenRewrittenToTest() throws Exception {
client
.get()
.uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.value()
.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)
.exchange(BodyInserters.fromFormData(formData))
.expectStatus()
.isOk()
.expectBody(String.class)
.value()
.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)
.exchange(BodyInserters.fromFormData(formData))
.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)
.exchange(fromResource(resource))
.expectStatus()
.isOk()
.expectBody(String.class)
.value()
.isEqualTo(String.valueOf(resource.contentLength()));
}

@Test
public void givenActors_whenAddActor_thenAdded() throws Exception {
client
.get()
.uri("/actor")
.exchange()
.expectStatus()
.isOk()
.expectBody(Actor.class)
.list()
.hasSize(2);

client
.post()
.uri("/actor")
.exchange(fromObject(new Actor("Clint", "Eastwood")))
.expectStatus()
.isOk();

client
.get()
.uri("/actor")
.exchange()
.expectStatus()
.isOk()
.expectBody(Actor.class)
.list()
.hasSize(3);
}

@Test
public void givenResources_whenAccess_thenGot() throws Exception {
client
.get()
.uri("/files/hello.txt")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.value()
.isEqualTo("hello");
}

}
Binary file added spring-5/src/test/resources/baeldung-weekly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.