Block Image

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:

  1. non c'è più la necessità di usare l'annotation @EnableSwagger2
  2. il tipo di documentazione è DocumentationType.OAS_30 e non più DocumentationType.SWAGGER_2
  3. 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:

Block Image

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:

  • 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