Skip to content

zialab/spring-reactive-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Reactive Sample

[toc]

The upcoming Spring 5 embraces Reactive Streams. From the offcial website of Reactive Streams:

Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure.This encompasses efforts aimed at runtime environments (JVM and JavaScript) as well as network protocols.

Currently, the JVM specification is completed, it includes a Java API(four simple interface), a textual Specification, a TCK and implementation examples. Check Reactive Streams for JVM for more details.

Reactor and RxJava2 implement this specification, and it is also adopted in Java 9 by the new Flow API.

For Spring developers, it brings a complete new programming model. In this post, we will try to cover all reactive features in the Spring projects.

  • Spring core framework added a new spring-webflux module, and provided built-in reactive programming support via Reactor and RxJava.
  • Spring Security 5 also added reactive feature.
  • In Spring Data umbrella projects, a new ReactiveSortingRepository interface is added in Spring Data Commons. Redis, Mongo, Cassandra subprojects firstly got reactive supports. Unluckily due to the original JDBC is desginated for blocking access, Spring Data JPA can not benefit from this feature.
  • Spring Session also began to add reactive features, an reactive variant for its SessionRepository is included in the latest 2.0.0.M3.

NOTE: At the moment I am writing this post, some projects are still under active development, I will update the content and the sample codes according to the final release version. Please start Github sample repository to track it.

Create a Webflux application

An example exceeds thousands of words. Let's begin to write some codes and enjoy the reactive programming brought by Spring 5.

Generally, I would like reuse the same concept in my former Spring Boot sample codes which is a simple blog application.

In the following steps we will start with creating RESTful APIs for Post.

Prerequisites

Make sure you have installed:

NOTE: Do not forget to add your Java and Maven command into your system environment variable PATH .

Prepare project skeleton

Execute the following command to create a general web application from Maven archetype.

$ mvn archetype:generate -DgroupId=com.example
	-DartifactId=demo
	-DarchetypeArtifactId=maven-archetype-webapp
	-DinteractiveMode=false

You can import the generated codes into your IDEs for further development.

Open pom.xml in your editor, add some modifications:

  1. Add spring-boot-starter-parent as parent POM to manage the versions of all required dependencies for this project.
  2. Add spring-webflux, jackson-databind, reactor-core as dependencies for Spring Web Reactive support
  3. Add logback as logging framework, jcl-over-slf4j is a bridge for Spring jcl and slf4j.
  4. Use Lombok to erase the tedious getters, setters, etc for a simple POJO class, check the Lombok project to get more information if you have Lombok trouble in your IDEs.
  5. You have to add spring milestone repositories in repositories and pluginRepositories, because at the moment, they are still in active development, and not availble in the official Maven public repository.

The final pom.xml looks like:

<?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.example</groupId>
    <artifactId>spring-reactive-sample-vanilla</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-reactive-sample-vanilla</name>
    <description>Spring Webflux demo(without Spring Boot)</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.M3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>     
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-buffer</artifactId>
        </dependency>
        
        <dependency>
            <groupId>io.projectreactor.ipc</groupId>
            <artifactId>reactor-core</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        
    </dependencies>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

Getting started

The project skeleton is ready, now let's add some codes to play reactive programming.

Create a new class named Post, it includes three fields: id, title, content.

@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Post {
    
    private Long id;
    private String title;
    private String content;
    
}

@Data, @ToString, @Builder, @NoArgsConstructor, @AllArgsConstructor are from the Lombok project.

When you compile Post, it will utilize Java compiler built-in Annotation Processing tooling to add extra facilities into the final compiled classes, including:

  1. Getters and setters of the three fields, and overrides equals and hashCode methods.
  2. Overrides toString method.
  3. A builder class for creating the Post instance more easily.
  4. A constructor with no arguments.
  5. A constructor with all fields as arguments.

Create a dummy repository named PostRepository to retrieve posts from and save them back to a repository.

@Component
class PostRepository {

    private static final Map<Long, Post> DATA = new HashMap<>();
    private static long ID_COUNTER = 1L;

    static {
        Arrays.asList("First Post", "Second Post")
            .stream()
            .forEach((java.lang.String title) -> {
                long id = ID_COUNTER++;
                DATA.put(Long.valueOf(id), Post.builder().id(id).title(title).content("content of " + title).build());
            }
            );
    }

    Flux<Post> findAll() {
        return Flux.fromIterable(DATA.values());
    }

    Mono<Post> findById(Long id) {
        return Mono.just(DATA.get(id));
    }

    Mono<Post> createPost(Post post) {
        long id = ID_COUNTER++;
        post.setId(id);
        DATA.put(id, post);
        return Mono.just(post);
    }

}

Currently we do not connect to any database, use a Map backed data store instead. When we talk about the Spring Data reactive feature later, we will replace it with a real Spring Data reactive implementation.

If you have used Spring Data before, you will find these APIs are every similiar with Repository interface provided in Spring Data.

The main difference is in the current Repository class all methods return a Flux or Mono instead.

Flux and Mono are from Reactor, which powers the reactive support in Spring 5 by default.

  • Flux means it could return lots of results in the stream.
  • Mono means it could return 0 to 1 result.

Create a controller class named PostController to expose RESTful PAIs for Post.

@RestController
@RequestMapping(value = "/posts")
class PostController {
    
    private final PostRepository posts;

    public PostController(PostRepository posts) {
        this.posts = posts;
    }

    @GetMapping(value = "")
    public Flux<Post> all() {
        return this.posts.findAll();
    }

    @GetMapping(value = "/{id}")
    public Mono<Post> get(@PathVariable(value = "id") Long id) {
        return this.posts.findById(id);
    }
    
    @PostMapping(value = "")
    public Mono<Post> create(Post post) {
        return this.posts.createPost(post);
    }
   
}

Create a @configuration class, add an @EnableWebFlux annotation to activiate webflux in this application.

@Configuration
@ComponentScan
@EnableWebFlux
class WebConfig {
    
}

Now we almost have done the programming work, let's try to bootstrap the application.

Bootstrap

According to the official documention, in WebFlux framework section, there are some options to bootsrap a reactive web application.

WebFlux can run on Servlet containers with support for the Servlet 3.1 Non-Blocking IO API as well as on other async runtimes such as Netty and Undertow.

Spring Boot initializer

Apache Tomcat

Create a general main class to run the application programticially.

ApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class, SecurityConfig.class);  

HttpHandler handler = DispatcherHandler.toHttpHandler(context);  

// Tomcat and Jetty (also see notes below)
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(handler); 

Tomcat tomcatServer = new Tomcat();
tomcatServer.setHostname(DEFAULT_HOST);
tomcatServer.setPort(DEFAULT_PORT);
Context rootContext = tomcatServer.addContext("", System.getProperty("java.io.tmpdir"));
Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet);
rootContext.addServletMapping("/", "httpHandlerServlet");
tomcatServer.start();

The above codes perform some tasks.

  1. Create a HttpHandler from ApplicationContext.
  2. Use ServletHttpHandlerAdapter to bridge the Servlet APIs to reactive based HttpHandler.
  3. Start tomcat server.

Do not forget add the tomcat-embed-core to project dependencies.

 <dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-core</artifactId>
</dependency>

You can simply run this class in IDEs as others Java application projects.

If you want to package all dependencies into one jar and run the application in one line command java -jar filename, maven-assembly-plugin can help this purpose.

Add maven-assembly-plugin configuration into the pom.xml file.

<!-- Maven Assembly Plugin -->
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-assembly-plugin</artifactId>
	<configuration>
		<descriptorRefs>
			<descriptorRef>jar-with-dependencies</descriptorRef>
		</descriptorRefs>
		<!-- MainClass in mainfest make a executable jar -->
		<archive>
			<manifest>
				<mainClass>com.example.demo.App</mainClass>
			</manifest>
		</archive>

	</configuration>
	<executions>
		<execution>
			<id>make-assembly</id>
			<phase>package</phase> <!-- bind to the packaging phase -->
			<goals>
				<goal>single</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Eneter the project root folder, execute the following command:

mvn package

When it is done, switch to the target folder, besides the general jar, you will find an extra fat jar was generated, which filename is ended with jar-with-dependencies.jar.

spring-reactive-sample-vanilla-0.0.1-SNAPSHOT-jar-with-dependencies.jar
spring-reactive-sample-vanilla-0.0.1-SNAPSHOT.jar

Run the following command to run this application.

java -jar target/XXXX-jar-with-dependencies.jar 

When it is started, try to fetch posts.

#curl http://localhost:8080/posts
[{"id":1,"title":"First Post","content":"content of First Post"},{"id":2,"title":"Second Post","content":"content of Second Post"}]

Alternatively, you can run the application in Reactor Netty, or JBoss Undertow.

Reactor Netty

For Reactor Netty, replace the above tomcat bootstraping codes with:

ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create(DEFAULT_HOST, DEFAULT_PORT).newHandler(adapter).block();

And add reactor-netty in your project dependencies.

<dependency>
	<groupId>io.projectreactor.ipc</groupId>
	<artifactId>reactor-netty</artifactId>
</dependency>

Undertow

For Undertow, replace the above tomcat bootstraping codes with:

UndertowHttpHandlerAdapter undertowAdapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(DEFAULT_PORT, DEFAULT_HOST).setHandler(undertowAdapter).build();
server.start();

And add undertow-core in your project dependencies.

<dependency>
	<groupId>io.undertow</groupId>
	<artifactId>undertow-core</artifactId>
</dependency>

Standalone Servlet Container

If you are stick on traditional web applications, and want to package it into a war file and deploy it into an existing servlet container, Spring 5 provides a AbstractAnnotationConfigDispatcherHandlerInitializer to archive this purpose. It is a standard Spring ApplicationInitializer implementation which can be scanned by Spring container when servlet container starts up.

Replace the above bootstraping class with:

public class AppIntializer extends AbstractAnnotationConfigDispatcherHandlerInitializer {

    @Override
    protected Class<?>[] getConfigClasses() {
        return new Class[]{
            WebConfig.class,
            SecurityConfig.class
        };
    }
}

And change the project packaging from jar to war in pom.xml.

<packaging>war</packaging>

Now you can run this application on a IDE managed Servlet 3.1 Container directly.

Or package the project into a war format and deploy it into a servlet 3.1 based container(tomcat, jetty) manually.

Alternatively, if you want to run this application via mvn command in the development stage. cargo-maven2-plugin can archive this purpose.

<plugin> 
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-war-plugin</artifactId>
	<configuration>
		<failOnMissingWebXml>false</failOnMissingWebXml>
	</configuration>
</plugin>
<plugin>
	<groupId>org.codehaus.cargo</groupId>
	<artifactId>cargo-maven2-plugin</artifactId>
	<configuration>
		<container>
			<containerId>tomcat8x</containerId>
			<type>embedded</type>
		</container>

		<configuration>
			<properties>
				<cargo.servlet.port>9000</cargo.servlet.port>
				<cargo.logging>high</cargo.logging>
			</properties>
		</configuration>
	</configuration>
</plugin>

Run the following command to package and deploy it into an embedded tomcat controlled by cargo.

mvn verify cargo:run

Spring Boot

Currently Spring Boot 2.0 is still in active development. The final Spring Boot 2.0 will target the latest Spring technology stack, including Spring 5, Spring Security 5, Spring Session 2 etc.

Open browser and navigate to http://start.spring.io.

Spring Boot initializer

In the Spring Boot Initializer page.

  1. Select Spring Boot version as 2.0.0.M3 or 2.0.0.SNAPSHOT.
  2. In the dependencies box, type reactive, it will display all reactive options in a dropdown menu. Select Ractive Web to add spring-webflux into project dependencies. You can also add other items as you like, such as Reactive MongoDb, Reactive Redis etc.
  3. Click Generate project button or hint ALT+NETER keys to generate a project skeleton as a zip file for downloading.

Download and extract it into your disc, import the source codes into your IDEs.

As you see, in the pom.xml, new Spring Boot strater spring-boot-starter-webflux is added.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

And the `` In a Spring Boot application, spring-boot-starter-webflux will handle the `spring-webflux` related dependencies and enable webflux automatically.

Reuse the former codes,

  1. No need WebConfig, Spring Boot configures it for you.
  2. The former bootstraping class or ApplicationInitializer is no use now, the Spring Boot's @SpringBootApplication annotated class hands over the application bootstrap.
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

By default, Spring Boot will use React Netty to a webflux application. No need extra configuration for it.

Use Spring Boot maven plugin to run this application.

mvn spring-boot:run

Reactive Data Operations

The next generation of Spring Data will embrace Reactive Streamse, and new reactive supported Repository and Template are added in the Spring Data Commons.

Currently, three subprojects(Redis, MongoDB, Cassandra) have implemented these interfaces, and got Reactive programming at the first time.

Here we use Spring Data MongoDB as an example, and convert the above Map based repsoitory to MongoDB driven repository.

Based on the former Spring Boot sample, add spring-boot-starter-data-mongodb-reactive into the project dependencies.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

This reactive Spring Boot starter will enable reactive support for MongoDB in this project.

Create a new Post MongoDB document class.

@Document
@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Post {

    @Id
    private String id;
    private String title;
    private String content;


}
  1. @Document declares it as a MongoDB document.
  2. @Id indicates it is the id of Post document.

Delcares PostRepository interface to extend Sprign Data MongoDB specific ReactiveMongoRepository.

interface PostRepository extends ReactiveMongoRepository<Post, String> {
}

Configure MongoDB connection in the appliation.yml file.

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/blog

Before starting up your application, make sure there is a running MongoDB instance in your local system.

NOTE: If you have not installed it, go to Mongo download page and get a copy of MongoDB, and install it into your system.

Alternatively, if you are familiar with Docker, it is simple to start a MongoDB instance via Docker Compose file.

version: '3.3' # specify docker-compose version

# Define the services/containers to be run
services:

  redis:
    image: redis
    ports:
      - "6379:6379"
      
  mongodb: 
    image: mongo 
    volumes:
      - mongodata:/data/db
    ports:
      - "27017:27017"
    command: --smallfiles --rest
#   command: --smallfiles --rest --auth  

volumes:
  mongodata:  

Execute docker-compose up to start a MongoDB instance in a Docker container.

Now it is ready for starting up the application.

mvn spring-boot:run

Data Auditing Support

Spring Data Mongo supports data auditing as Spring Data JPA, it can set the current user and created/last modified timestamp to a field automatically.

Add EnableMongoAuditing to application class to activiate auditing for MongoDB.

@EnableMongoAuditing
public class DemoApplication {}

In Post document, add a new field createdDate, annotated it with @CreatedDate, it will fill the createdDate with current date when inserting it into MongoDB.

@CreatedDate
private LocalDateTime createdDate;

Data initialition

Add some test datas into MongoDB when it starts up.

@Component
@Slf4j
class DataInitializr implements CommandLineRunner {

    private final PostRepository posts;

    public DataInitializr(PostRepository posts) {
        this.posts = posts;
    }

    @Override
    public void run(String[] args) {
        log.info("start data initialization  ...");
        this.posts
            .deleteAll()
            .thenMany(
                Flux
                    .just("Post one", "Post two")
                    .flatMap(
                        title -> this.posts.save(Post.builder().title(title).content("content of " + title).build())
                    )
            )
            .log()
            .subscribe(
                null,
                null,
                () -> log.info("done initialization...")
            );

    }

}

Use a CommandLineRunner to make sure the run method is executed after the application is started.

Execute mvn spring-boot:run to start up the application now, then we can test if the data is initialized successfully.

curl -v http://localhost:8080/posts
* timeout on name lookup is not supported
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /posts HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.1
> Accept: */*
>
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: application/json;charset=UTF-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
<
[{"id":"599149d53c44062e08c58b86","title":"Post one","content":"content of Post one","createdDate":[2017,8,14,14,57,25,71000000]},{"id":"599149d53c44062e08c58b87","title":"Post two","content":"content of Post two","createdDate":[2017,8,14,14,57,25,173000000]}]* Connection #0 to host localhost left intact

As you see, the data is initialized and createdDate is inserted automatically.

Security for Webflux

Reflect to new webflux feature introduced in Spring 5, Spring Security 5 added new APIs to handle Reactive Web security.

Similar with Spring 5, Spring Security 5 added a new module named spring-secuirty-webflux.

Add it in the project dependencies aside with spring-boot-starter-security.

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-webflux</artifactId>
</dependency>

NOTE: Currently you have to add spring-security-webflux explicitly, there is no specific starters for spring-security-webflux.

Add @EnableWebFluxSecurity to a configuration class to enable Spring security for Webflux.

@EnableWebFluxSecurity
class SecurityConfig {

	@Bean
	SecurityWebFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
		return http
			.authorizeExchange()
				.pathMatchers(HttpMethod.GET, "/posts/**").permitAll()
                .pathMatchers(HttpMethod.DELETE, "/posts/**").hasRole("ADMIN")
				//.pathMatchers("/users/{user}/**").access(this::currentUserMatchesPath)
				.anyExchange().authenticated()
				.and()
			.build();
	}

	private Mono<AuthorizationDecision> currentUserMatchesPath(Mono<Authentication> authentication, AuthorizationContext context) {
		return authentication
			.map( a -> context.getVariables().get("user").equals(a.getName()))
			.map( granted -> new AuthorizationDecision(granted));
	}

	@Bean
	public MapUserDetailsRepository userDetailsRepository() {
		UserDetails rob = User.withUsername("test").password("test123").roles("USER").build();
		UserDetails admin = User.withUsername("admin").password("admin123").roles("USER","ADMIN").build();
		return new MapUserDetailsRepository(rob, admin);
	}

}
  1. Use @EnableWebFluxSecurity annotation to enable Security for spring-webflux based application.
  2. SecurityWebFilterChain bean is a must to configure the details of Spring Security. HttpSecurity is from spring-secuirty-webflux, similar with the general version, but handle WebExhange instead of Servlet based WebRequest.
  3. A new UserDetailsRepository interface is introduced which is aligned with Reactor APIs. By default, an in-memory Map based implementation MapUserDetailsRepository is provided, you can customsize yourself by implementing the UserDetailsRepository interface.

Starts up the application and verify the Spring Security configuratoin work as expected.

mvn spring-boot:run

After it is started, try to add a new post without authentication:

#curl -v  -X POST http://localhost:8080/posts -H "Content-Type:application/json" -d "{\"title\":\"My Post\",\"content\":\"content of My Post\"}"
Note: Unnecessary use of -X or --request, POST is already inferred.
* timeout on name lookup is not supported
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /posts HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.1
> Accept: */*
> Content-Type:application/json
> Content-Length: 42
>
* upload completely sent off: 42 out of 42 bytes
< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Basic realm="Realm"
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< content-length: 0
<
* Connection #0 to host localhost left intact

The server side rejects the client request, and sends back a 401 error(401 Unauthorized).

Use the predefined test:test123 credentials to get authenticated and send the post request again.

curl -v  -X POST http://localhost:8080/posts -u "test:test123" -H "Content-Type:application/json" -d "{\"title\":\"My Post\",\"content\":\"content of My Post\"}"
Note: Unnecessary use of -X or --request, POST is already inferred.
* timeout on name lookup is not supported
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'test'
> POST /posts HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dGVzdDp0ZXN0MTIz
> User-Agent: curl/7.54.1
> Accept: */*
> Content-Type:application/json
> Content-Length: 50
>
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 200 OK
< transfer-encoding: chunked
< Content-Type: application/json;charset=UTF-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< set-cookie: SESSION=b99124f7-c0a0-4507-b9be-34718af3d137; HTTPOnly
<
{"id":"59906f9d3c44060e044fb378","title":"My Post","content":"content of My Post","createdDate":[2017,8,13,23,26,21,392000000]}* Connection #0 to host localhost left intact

It is done secussfully, and returns the new created post.

Dive into Funcational Programming

RouterFunction

Kotlin

Client

Test

Put all together

Sample codes

The following table lits all sample codes related to this post.

name description
vanilla The initial application, includes basic spring-webflux feature, use a main class to start up the application
war Replace the manual bootstrap class in vanilla with Spring ApplicationInitializer, it can be packaged as a war file to be deployed into an external servlet container.
boot Switch to Spring Boot to get autoconfiguration of webflux, added Spring Data Mongo, Spring Secuirty support
boot-routes Use RouterFunction instead of the general Controller in boot
session More features will be added here

References

About

Spring 5 Reactive playground

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 86.3%
  • Kotlin 6.4%
  • Groovy 4.1%
  • HTML 2.4%
  • Other 0.8%