Kotlin Spring WebFlux Annotation-Based RestAPIs – with SpringBoot 2

Kotlin Spring WebFlux Annotation-Based RestAPIs – with SpringBoot 2

Reactive programming is about non-blocking applications. And Spring Framework 5 includes a new spring-webflux module, supports Reactive Streams for communicating backpressure across async components and libraries. So in the tutorial, JavaSampleApproach will guide you through the steps for creating a Kotlin Spring WebFlux Annotation-Based RestAPIs with SpringBoot 2.

Related posts:
Kotlin Spring MVC RequestMapping RESTful APIs with @GetMapping, @PostMapping, @PutMapping, @DeleteMapping | SpringBoot Example

I. Technologies

– Java: 1.8
– Maven: 3.5.2
– Spring Tool Suite: Version 3.9.0.RELEASE
– Spring Boot: 2.0.0.M7
– Kotlin 1.1.61
– Spring Boot Starter Webflux
– Postman client

II. Spring WebFlux

Spring Framework 5.0 supports WebFlux with fully asynchronous and non-blocking and does NOT require the Servlet API(Unlike Spring MVC).

Spring WebFlux supports 2 distinct programming models:
– Annotation-based with @RestController
– Functional with Java 8 lambda style

In the tutorial, we will introduce WebFlux with Annotation-based and Kotlin language.
For starting with WebFlux, SpringBoot supports a collection dependency: spring-boot-starter-webflux.

Kotlin sample code:


import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping(value="/api/customer")
class RestControllerAPIs {

	@GetMapping
    fun getAll(): Flux {
    	return Flux.fromIterable(ArrayList(custStores.values));
    }
	
	@GetMapping("/{id}")
    fun getCustomer(@PathVariable id: Long): Mono {
		return Mono.justOrEmpty(custStores.get(id))
    }
	
	...
}

reactor.core.publisher.Flux: is a standard Publisher representing a reactive sequence of 0..N items, optionally terminated by either a success signal or an error.
reactor.core.publisher.Mono: Mono is a specialized Publisher that emits at most single-valued-or-empty result.

III. Practice

In the tutorial, We create a Kotlin SpringBoot 2 project as below:

Kotlin Spring WebFlux AnnotationBased RestAPIs - project structure

Step to do:
– Create Kotlin SpringBoot project
– Create data models
– Implement Spring WebFlux APIs
– Run and check results

1. Create Kotlin SpringBoot project

Using SpringToolSuite, create a Kotlin SpringBoot 2 project with Reactive Web dependency spring-boot-starter-webflux:

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

<properties>
	<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
	<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.boot</groupId>
		<artifactId>spring-boot-starter-webflux</artifactId>
	</dependency>
	<dependency>
		<groupId>org.jetbrains.kotlin</groupId>
		<artifactId>kotlin-stdlib-jre8</artifactId>
	</dependency>
	<dependency>
		<groupId>org.jetbrains.kotlin</groupId>
		<artifactId>kotlin-reflect</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>io.projectreactor</groupId>
		<artifactId>reactor-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

<build>
	<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
	<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
		<plugin>
			<artifactId>kotlin-maven-plugin</artifactId>
			<groupId>org.jetbrains.kotlin</groupId>
			<configuration>
				<compilerPlugins>
					<plugin>spring</plugin>
				</compilerPlugins>
			</configuration>
			<dependencies>
				<dependency>
					<groupId>org.jetbrains.kotlin</groupId>
					<artifactId>kotlin-maven-allopen</artifactId>
					<version>${kotlin.version}</version>
				</dependency>
			</dependencies>
		</plugin>
	</plugins>
</build>

<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>

2. Create data models

– Create Address data model:


package com.javasampleapproach.webflux.model

data class Address(
		var street : String? = null,
		var postcode : String? = null
){}

– Create Customer data model:


package com.javasampleapproach.webflux.model

data class Customer(
		var id: Long = -1,
		var firstName: String? = null,
		var lastName: String? = null,
		var age: Int? = null,
		var address: Address = Address()) {
}

3. Implement Spring WebFlux APIs


package com.javasampleapproach.webflux.controller

import javax.annotation.PostConstruct

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
 
import com.javasampleapproach.webflux.model.Customer
import com.javasampleapproach.webflux.model.Address
 
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping(value="/api/customer")
class RestControllerAPIs {
	
	val log = LoggerFactory.getLogger(RestControllerAPIs::class.java);
	
	// Define customer storage
	val custStores = mutableMapOf()
	
	@PostConstruct
    fun initial(){
        custStores.put(1, Customer(1, "Jack", "Smith", 20, Address("NANTERRE CT", "77471")))
        custStores.put(2, Customer(2, "Peter", "Johnson", 25, Address("W NORMA ST", "77009")))
    }
	
	@GetMapping
    fun getAll(): Flux {
    	return Flux.fromIterable(ArrayList(custStores.values));
    }
	
	@GetMapping("/{id}")
    fun getCustomer(@PathVariable id: Long): Mono {
		return Mono.justOrEmpty(custStores.get(id))
    }
	
	@PostMapping("/post")
    fun postCustomer(@RequestBody customer: Customer): Mono>{
		// do post
		custStores.put(customer.id, customer)
		
		log.info("########### POST:" + customer)
		
        return Mono.just(ResponseEntity("Post Successfully!", HttpStatus.CREATED))
    }
	
    @PutMapping("/put/{id}")
    fun putCustomer(@PathVariable id: Long, @RequestBody customer: Customer): Mono>{
		// reset customer.Id
		customer.id = id;
		
		if(custStores.get(id) != null){
			custStores.replace(id, customer)
		}else{
			customer.id = id
			custStores.put(id, customer)
		}
		
		log.info("########### PUT:" + customer)
		return Mono.just(ResponseEntity(customer, HttpStatus.CREATED))
    }
	
	@DeleteMapping("/delete/{id}")
    fun deleteCustomer(@PathVariable id: Long): Mono> {
		val cust = custStores.remove(id)
		if(cust != null){
			log.info("########### DELETE:" + cust)
		}else{
			log.info("########### Don't exist any customer with id = ${id}")
		}
		
		return Mono.just(ResponseEntity("Delete Succesfully!", HttpStatus.ACCEPTED))
    }
}

4. Run and check results

Run the Kotlin SpringBoot project with commandline: mvn spring-boot:run

-> See console’ logs:


s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/api/customer],methods=[GET]}" onto public reactor.core.publisher.Flux com.javasampleapproach.webflux.controller.RestControllerAPIs.getAll()
s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/api/customer/delete/{id}],methods=[DELETE]}" onto public reactor.core.publisher.Mono> com.javasampleapproach.webflux.controller.RestControllerAPIs.deleteCustomer(long)
s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/api/customer/{id}],methods=[GET]}" onto public reactor.core.publisher.Mono com.javasampleapproach.webflux.controller.RestControllerAPIs.getCustomer(long)
s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/api/customer/post],methods=[POST]}" onto public reactor.core.publisher.Mono> com.javasampleapproach.webflux.controller.RestControllerAPIs.postCustomer(com.javasampleapproach.webflux.model.Customer)
s.w.r.r.m.a.RequestMappingHandlerMapping : Mapped "{[/api/customer/put/{id}],methods=[PUT]}" onto public reactor.core.publisher.Mono> com.javasampleapproach.webflux.controller.RestControllerAPIs.putCustomer(long,com.javasampleapproach.webflux.model.Customer)

– Make a GET all customer request: http://localhost:8080/api/customer

Kotlin Spring WebFlux AnnotationBased RestAPIs - get all customers

– Make a GET customer request: http://localhost:8080/api/customer/1

Kotlin Spring WebFlux AnnotationBased RestAPIs - get a customer

– Make a POST request: http://localhost:8080/api/customer/post

Kotlin Spring WebFlux AnnotationBased RestAPIs - post a customer

– Make a PUT request: http://localhost:8080/api/customer/put/3

Kotlin Spring WebFlux AnnotationBased RestAPIs - put a customer

– Make a DELETE request: http://localhost:8080/api/customer/delete/1

Kotlin Spring WebFlux AnnotationBased RestAPIs - delete a customer

– Make a GET all customers request again: http://localhost:8080/api/customer

Kotlin Spring WebFlux AnnotationBased RestAPIs - get all customers

-> Logs from Kotlin SpringBoot server:


c.j.w.controller.RestControllerAPIs      : ########### POST:Customer(id=3, firstName=Mary, lastName=Taylor, age=27, address=Address(street=S NUGENT AVE, postcode=77571))
c.j.w.controller.RestControllerAPIs      : ########### PUT:Customer(id=3, firstName=Amy, lastName=Taylor, age=24, address=Address(street=E NAVAHO TRL, postcode=77449))
c.j.w.controller.RestControllerAPIs      : ########### DELETE:Customer(id=1, firstName=Jack, lastName=Smith, age=20, address=Address(street=NANTERRE CT, postcode=77471))

IV. Sourcecode

SpringBootKotlinWebFluxAnnotationBased



By grokonez | December 10, 2017.

Last updated on April 4, 2021.



Related Posts


Got Something To Say:

Your email address will not be published. Required fields are marked *

*