La versione di Swagger 3 della libreria springfox porta tante novità, tra cui una facile
integrazione con OpenAPI.
OpenAPI è una specifica che permette di standardizzare la rappresentazione di API REST.
Passo 1: importiamo la seguente dipendenza
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Se stai migrando da Swagger 2, eliminare le altre dipendenze. Questa dipendenza infatti permette di integrare swagger, la sua ui e anche le API di Spring Data Rest.
Passo 2: creare una classe di configurazione di Swagger
@Configuration
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.forCodeGeneration(true);
}
}
Notiamo 2 cose:
- non c'è più la necessità di usare l'annotation @EnableSwagger2
- il tipo di documentazione è DocumentationType.OAS_30 e non più DocumentationType.SWAGGER_2
- il metodo forCodeGeneration ci permette di avere una giusta formattazione della documentazione di swagger per
autogenerare il client senza riscontrare problemi di caratteri
Passo 3: proviamo l'UI di swagger dal browser
Rispetto a swagger 2, l'endpoint di default non è più /swagger-ui.html ma /swagger-ui/
Io per semplicità ho preso il progetto di un precedente tutorial: Spring Boot REST
che ha una entity User con un JpaRepository che fa anche da API Resource.
Andando su http://localhost:8080/swagger-ui/ otteniamo una schermata simile a questa:
Se andiamo su http://localhost:8080/v3/api-docs troviamo la documentazione delle nostre API che rispetta la specifica OpenAPI:
{
"openapi": "3.0.3",
"info": {
"title": "Api Documentation",
"description": "Api Documentation",
"termsOfService": "urn:tos",
"contact": {},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
},
"version": "1.0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Inferred Url"
}
],
"tags": [
{
"name": "User Entity",
"description": "Simple Jpa Repository"
}
],
...
Autogenerazione di un client REST da Swagger
Passo 1: aggiungiamo il seguente plugin nel pom:
<profiles>
<profile>
<id>rest-client</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.3.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/swagger.json</inputSpec>
<generatorName>java</generatorName>
<skipValidateSpec>true</skipValidateSpec>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.threeten</groupId>
<artifactId>threetenbp</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>io.gsonfire</groupId>
<artifactId>gson-fire</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
</profile>
</profiles>
Passo 2: aggiungiamo il file swagger.json
All'interno della cartella root del progetto, creiamo il file swagger.json. Copiamo la response di http://localhost:8080/v3/api-docs all'interno del file. In alternativa, possiamo autogenerare il file in vari modi; qui ho creato una classe di test che genera il file:
package it.enzoracca.springbootapp;
import com.fasterxml.jackson.databind.JsonNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import java.io.File;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Ignore
public class SwaggerGenerator {
@Autowired
private TestRestTemplate testRestTemplate;
@Test
public void generateSwagger() throws Exception {
JsonNode response = testRestTemplate.getForObject("http://localhost:8080/v3/api-docs", JsonNode.class);
FileUtils.writeStringToFile(new File("swagger.json"), response.toPrettyString());
}
}
In questa classe ho usato FileUtils di apache-commons, aggiungendo la seguente dipendenza maven:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
Una volta creato il file, buildiamo il progetto con: mvn clean install -Prest-client.
Dopo aver buildato, troveremo all'interno di
target/generated-sources/openapi/src/gen/java/main/api le api client autogenerate.
Ad esempio, avendo nel progetto una resource UserEntity (che rappresenta le API di UserRepository),
troveremo una classe UserEntityApi con tutti i relativi metodi.
Inoltre, in target/generated-sources/openapi/api troveremo il file openapi.yaml autogenerato.
Passo 3: proviamo il client
Per semplicità, per provare il client autogenerato, all'interno del progetto creiamo un sottopackage client con la seguente classe:
public class ClientDemo {
public static void main(String... args) throws ApiException {
UserEntityApi userEntityApi = new UserEntityApi();
User user = new User();
user.setName("Mario");
user.setSurname("Rossi");
EntityModelOfUser userRet = userEntityApi.saveUserUsingPOST(user);
System.out.println("UTENTE INSERITO: " + userRet);
CollectionModelOfUser allUsers = userEntityApi.findAllUserUsingGET(null, null, null);
System.out.println("GET ALL: " + allUsers);
}
}
Avremo un output simile:
UTENTE INSERITO: class EntityModelOfUser {
address: null
id: null
links: null
name: Mario
surname: Rossi
}
GET ALL: class CollectionModelOfUser {
embedded: class EmbeddedCollectionOfUser {
users: [class User {
address: null
id: null
name: Mario
surname: Rossi
}]
}
links: {self=class Link {
deprecation: null
href: http://localhost:8080/users
hreflang: null
media: null
name: null
profile: null
rel: null
title: null
type: null
}, profile=class Link {
deprecation: null
href: http://localhost:8080/profile/users
hreflang: null
media: null
name: null
profile: null
rel: null
title: null
type: null
}}
}
Conclusioni
In pochi passi abbiamo importato/aggiornato Swagger con la versione 3 e la specifica OpenAPI e abbiamo autogenerato un client REST.
Potete trovare il progetto completo sul mio github a questo link: Spring Boot OpenAPI
Articoli su Spring: Spring
Libri consigliati su Spring:
- Cloud Native Spring in Action: https://amzn.to/3xZFg1S
- Pro Spring 5 (Spring da zero a hero): https://amzn.to/3KvfWWO
- Pivotal Certified Professional Core Spring 5 Developer Exam: A Study Guide Using Spring Framework 5 (per certificazione Spring): https://amzn.to/3KxbJSC
- Pro Spring Boot 2: An Authoritative Guide to Building Microservices, Web and Enterprise Applications, and Best Practices (Spring Boot del dettaglio): https://amzn.to/3TrIZic