Microservices mit REST-API in Kotlin und Spring Boot
Ein angenehmer Aspekt von Microservices ist, dass ihre Entwicklung häufig auf der grünen Wiese startet. Ein neuer Microservice ist also immer eine Chance einen neuen Technologie-Stack auszuprobieren. Für eingefleischte Java-Entwickler bietet sich dazu die Programmiersprache Kotlin an. Als JVM basierte Programmiersprache kann Kotlin problemlos das bei Java-Entwicklern beliebte Spring Framework verwenden. Daher zeige ich in diesem Artikel, wie ihr mit Spring Boot schnell einen Kotlin Microservice mit REST-API aufsetzt.
Neuer Microservice, die Gelegenheit mit Kotlin anzufangen...
1. IDE auswählen: IntelliJ vs. Eclipse
2. Kotlin Projekt mit Spring Boot aufsetzen
https://start.spring.io/ |
Im hier gezeigten Tutorial verwendete ich im Spring Initializr:
- Gradle als Build Tool
- Kotlin als Sprache 😉
- Spring Boot Version 2.6.5 (oder höher)
- jar als Ziel-Paket meiner Anwendung
- Java Version 11 für die unterliegende JVM (Programmiersprache ist Kotlin)
- Spring Web und Spring Boot DevTools als Dependencies
3. Kotlin Projekt starten
REST API in Kotlin mit Spring
Gradle Dependencies
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
developmentOnly("org.springframework.boot:spring-boot-devtools")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
Rest-API in Kotlin
- POST /item : Erstellt ein neues Item.
- GET /item?itemName=Ball : Sucht ein vorhandenes Item anhand seines Namens.
- DELETE /item/<itemId> : Löscht ein Item anhand seiner Id.
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.http.ResponseEntity.*
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("/item")
class ItemController {
val items = mutableListOf<Item>()
@PostMapping
fun addItem(@RequestBody item : Item): ResponseEntity<Unit> {
items.add(item)
return status(HttpStatus.CREATED).build()
}
...
}
- @RestController macht aus der Kotlin-Klasse einen Spring MVC Controller im Kontext des Design Patterns Model-View-Controller, siehe dazu auch: rest-json-apis-in-java.html. Der Controller wird vom standard-mäßig konfigurierten Spring RequestMappingHandler erkannt und verarbeitet.
- @RequestMapping Annotationen werden verwendet, um zu definieren auf welche http Methoden, URL-Pfade, Datentypen usw. die Klasse und oder die jeweilig annotierte Methode reagieren. Hier verarbeitet der Controller alle eingehenden Requests mit dem Pfad /item.
- @PostMapping entspricht dabei @RequestMapping ergänzt um die zu verwendende http POST-Methode, also: @RequestMapping(method = RequestMethod.POST). Die Annotation an der Methode übernimmt den definierten Pfad in der Klassen-Annotation als Prefix.
- @RequestBody gibt an, dass der Body des POST Requests im annotierten Parameter an die Methode übergeben wird. Der Request-Body ist im JSON-Format und wird von Spring automatisch mittels Jackson Bibliothek in die Datenklasse Item geparst. Weiter unten zeige ich den Code dieser Klasse - es ist vergleichbar zu einer einfachen POJO-Klasse in Java.
- ResponseEntity ist eine Klasse des Spring Frameworks, mit der eine http Response gebaut werden kann. Im Beispiel wird einfach der http Code 201 (CREATED) gesetzt und ein leerer http Response Body mit den Standard-Headern zurückgeschickt. Status, Body und Header können generell mit dieser Klasse gesetzt werden, so dass unser Controller mit entsprechenden http Responses antwortet.
- Zum Speichern der Item Objekte verwende ich eine veränderbare Liste, die ich mit der Kotlin-Methode mutableListOf<Item>() erzeugt habe. Unter der Haube verwendet Kotlin hier die Klasse ArrayList von Java.
http Methoden hinzufügen
- GET mittels Annotation @GetMapping
- POST mittels Annotation @PostMapping
- PUT mittels Annotation @PutMapping
- PATCH mittels Annotation @PatchMapping
- HEAD mittels @RequestMapping(method = RequestMethod.HEAD)
- DELETE mittels Annotation @DeleteMapping
- OPTIONS mittels @RequestMapping(method = RequestMethod.OPTIONS)
- TRACE mittels @RequestMapping(method = RequestMethod.TRACE)
@GetMapping
fun getItemByName(@RequestParam(required = true) itemName: String):
ResponseEntity<Item> =
items.stream().filter { it.name.equals(itemName, true) }.findFirst()
.map { ok(it) }.orElse(notFound().build())
@DeleteMapping("/{itemId}")
fun deleteItemById(@PathVariable itemId: String?):
ResponseEntity<Unit> =
if (items.removeIf{it.id == itemId}) noContent().build()
else notFound().build()
- @RequestParam ist eine weitere Spring Annotation, die verwendet wird, um Query-Parameter aus der URL in einen Methoden Parameter zu überführen.
Wird unser REST-Service mit folgender URL aufgerufen:
http://localhost:8080/item?itemName=Ball
so nimmt der String Parameter itemName automatisch den Wert "Ball" an. Wenn der Parametername mit dem Namen des Request-Parameters in der URL nicht übereinstimmt, bekommen wir automatisch einen Bad Request Fehler mit HTTP Code 400. - Mit ok(it) wird hier eine http Response mit dem Code 200 erstellt. Im Body dieser Response bzw. in der Variable it befindet sich die gefundene Item Instanz. it ist in Kotlin ein Keyword zur Vereinfachung von Lambda-Ausdrücken.
- Eine weitere Variante der Parameter-Übertragung von der URL in die public Methode (@PathVariable) wird in der mit @DeleteMapping annotierten Methode gezeigt. In @DeleteMapping wird zusätzlich ein Teil des URL-Pfades definiert, nämlich "/{itemId}". Im vorherigen Abschnitt haben wir gesehen, dass man auch in der @RequestMapping Annotation den URL-Pfad spezifizieren kann. Wenn dies auf Klassenebene geschieht, so gilt es für alle Methoden. Eine valide URL für unsere Delete-Methode würde so aussehen:
http://localhost:8080/item/123
Die in geschweiften Klammern notierten Teile der URL werden an Parameter mit der Annotation @PathVariable übergeben. In unserem Beispiel hätte der String Parameter itemId also den Wert "123". - Die hier gezeigten Annotationen lassen sich beliebig kombinieren, so dass man die REST-API wie benötigt implementieren kann.
Gegenüberstellung Kotlin und Java
Methoden Deklarationen sehen in Kotlin anders aus als in Java. Schaut euch Details dazu am besten in der Kotlin Dokumentation an: https://kotlinlang.org/docs/functions.html- Der Kotlin Code kann kompakter werden, weil Methoden-Rückgabewerte einfach mittels des Operators = zurückgegeben werden, wenn sie in einem Statement berechnet werden können. So werden geschweifte Klammern, return-Statements und Deklaration des Methoden-Rückgabetyps gespart. Das habe ich in getItemByName und deleteItemById gemacht.
- In Kotlin können if-Ausdrücke auch direkt einen Rückgabewert haben. In deleteItemById wird also das Ergebnis des if-Ausdrucks als Methodenergebnis zurückgegeben. Also hier eine ResponseEntity mit dem HTTP-Code 204 oder 404. Java entwickelt sich ebenfalls in diese Richtung, wie man anhand der in Java 14 eingeführten Neuerungen am switch-Statement sieht:
java-17-features.html - Die Item Klasse war in Java eine reine Datenklassen mit Getter- und Setter-Methoden für alle Attribute - also voll von Boilerplate-Code. Kotlin bietet data class Klassen an, die es uns ermöglichen alles in eine einzige Zeile zu schreiben und uns somit sämtlichen Boilerplate-Code abnehmen:
Item API ausprobieren
HTTP-POST-Request mit Postman an Spring Boot Anwendung geschickt |
Diese Postman Testsuite funktioniert sowohl bei der Kotlin als auch bei der Java Variante meiner Spring Boot REST-API. Die Spring Boot Anwendung muss vorher gestartet werden, das funktioniert so wie weiter oben beschrieben.
Kommentare