Współczesny rozwój aplikacji internetowych często opiera się na architekturze mikrousług, gdzie kluczową rolę odgrywają interfejsy API REST. Aby usprawnić tworzenie takich interfejsów oraz ich konsumpcję, możemy wykorzystać specyfikację OpenAPI w połączeniu z Feign – deklaratywnym klientem HTTP dla Spring Boot. W tym artykule przedstawiamy, jak zintegrować te narzędzia, aby zautomatyzować proces generowania kodu serwera i klienta w Java 17 i Spring Boot 3, a także zaprezentujemy przykład tworzenia i pobierania danych użytkownika.
Czym jest OpenAPI i Feign?
OpenAPI to otwarta specyfikacja do opisu interfejsów API REST, umożliwiająca automatyczne generowanie dokumentacji oraz kodu klienta i serwera. Feign to biblioteka Spring Cloud, która pozwala tworzyć deklaratywne klienty HTTP, upraszczając komunikację między mikrousługami. Korzystanie z Feign bardzo ułatwia pracę. Wystarczy do posiadanego kontraktu (opisanego w pliky yaml) wygenerować api klienckie i serwerowe. Ma to dodatkową, ogromną zaletę – nasz klient, czy serwer bazuje na kontrakcie. Na tym samym kontrakcie bazuje strona przeciwna (ta, z którą się komunikujemy). Jeśli obie strony komunikacji bazują na wspólnym kontrakcie, pozbywamy się ryzyka zmian w strukturze danych, które mogą wprowadzić błędy integracji. Olbrzymią zaletą jest również możliwość zastosowania testów kontraktowych. Otóż, przy budowaniu aplikacji, jeśli jesteśmy niezgodni z kontraktem – testy wysypią. Więcej o testach kontraktowych pisał Piotrek Pelczar – zachęcam gorąco do przeczytania artykułu – https://softwareskill.pl/consumer-driven-contract
Konfiguracja środowiska
Zacznijmy od przygotowania naszego środowiska deweloperskiego. Upewnij się, że masz zainstalowane:
- JDK 17
- Maven 3.6+
- Spring Boot 3
Generowanie serwera REST z OpenAPI
Aby wygenerować serwer należy zdefiniować kontrakt, na podstawie którego będzie generowany sam serwer. Dodamy potrzebne zależności do Mavena, aby na samym końcu zaimplementować interfejs wygenerowany przez OpenApi.
1. Tworzenie specyfikacji OpenAPI
Zdefiniuj specyfikację OpenAPI dla naszego API. Załóżmy, że tworzymy API użytkownika z endpointami do tworzenia i pobierania danych użytkownika. Specyfikację zapiszemy w pliku YAML (src/main/resources/user-api.yaml
):
openapi: 3.0.1
info:
title: User API
version: 1.0.0
paths:
/user:
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'201':
description: Successfully created user
get:
summary: Get user data
responses:
'200':
description: A user object
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
email:
type: string
2. Konfiguracja Maven do generowania kodu
Użyj pluginu openapi-generator-maven-plugin
w pom.xml
, aby wygenerować kod źródłowy na podstawie specyfikacji OpenAPI:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.2.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/user-api.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>pl.softwareskill.user.api</apiPackage>
<modelPackage>pl.softwareskill.user.model</modelPackage>
<configOptions>
<interfaceOnly>true</interfaceOnly>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Uruchom mvn clean package
, aby wygenerować kod.
3. Implementacja serwera
Zaimplementuj serwer, korzystając z wygenerowanych interfejsów. Dodaj logikę biznesową dla tworzenia i pobierania użytkownika:
package com.example.user.api;
import com.example.user.model.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
public class UserController implements UserApi {
@Override
public ResponseEntity<User> createUser(User user) {
user.setId(UUID.randomUUID().toString()); // Przykładowa implementacja
return ResponseEntity.status(201).body(user);
}
@Override
public ResponseEntity<User> getUser() {
User user = new User(); // Przykładowe dane
user.setId(UUID.randomUUID().toString());
user.setName("Jan Kowalski");
user.setEmail("jan.kowalski@example.com");
return ResponseEntity.ok(user);
}
}
Mamy już część serwerową, serwującą API na podstawie dostarczonego kontraktu. Teraz pora na utworzenie części klienckiej, którą również wygenerujemy na podstawie tego samego kontraktu.
Integracja z Feign do tworzenia klienta REST
1. Dodaj zależności Spring Cloud OpenFeign
W pom.xml
projektu dodaj zależność do Spring Cloud OpenFeign:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. Włącz Feign w aplikacji
Dodaj adnotację @EnableFeignClients
do głównej klasy aplikacji:
@SpringBootApplication
@EnableFeignClients
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
Zdefiniuj klienta Feign, wykorzystując interfejs wygenerowany przez OpenAPI (generowanie interfejsu odbywa się dokładnie tak samo jak w poprzednim przypadku), lub stwórz nowy interfejs z adnotacją @FeignClient
:
@FeignClient(name = "userClient", url = "http://localhost:8080")
public interface UserClient extends UserApi {
}
3. Wykorzystanie klienta Feign
Użyj klienta Feign w swojej aplikacji, aby komunikować się z API użytkownika:
@RestController
@RequestMapping("/api")
public class UserFeignController {
private final UserClient userClient;
public UserFeignController(UserClient userClient) {
this.userClient = userClient;
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userClient.createUser(user).getBody();
}
@GetMapping("/users")
public User getUser() {
return userClient.getUser().getBody();
}
}
Podsumowanie
Dzięki wykorzystaniu OpenAPI i Feign w Spring Boot możemy efektywnie generować kod serwera i klienta, co znacząco przyspiesza rozwój aplikacji opartych o architekturę mikrousług. Integracja tych narzędzi umożliwia nie tylko automatyzację wielu aspektów tworzenia oprogramowania, ale także zapewnia spójność i łatwość w utrzymaniu kodu. Wprowadzenie przykładu z użytkownikiem pokazuje, jak można zastosować te technologie do praktycznych zastosowań, umożliwiając szybkie tworzenie i pobieranie danych użytkownika w nowoczesnej aplikacji internetowej. Bazując na wspólnym kontrakcie pomiędzy klientem a serwerem, wykorzystując przy tym testy kontraktowe, zabezpieczamy naszą integrację przed omyłkowymi zmianami, które mogłyby zerwać kompatybilność integracji.
Gdybyś potrzebował jeszcze więcej:
Jesteś Java Developerem?
Przejdź na wyższy poziom wiedzy
„Droga do Seniora” 🔥💪
Jesteś Team Leaderem? Masz zespół?
Podnieś efektywność i wiedzę swojego zespołu 👌