Documenter une API Spring REST à l'aide d'OpenAPI 3.0

Haut REST

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Vue d'ensemble

La documentation est une partie essentielle de la création d'API REST. Dans ce didacticiel, nous examinerons SpringDoc - un outil qui simplifie la génération et la maintenance des documents API, basés sur la spécification OpenAPI 3, pour les applications Spring Boot 1.x et 2.x.

2. Configuration de springdoc-openapi

Pour que springdoc-openapi génère automatiquement la documentation de spécification OpenAPI 3 pour notre API, nous ajoutons simplement la dépendance springdoc-openapi-ui à notre pom.xml :

 org.springdoc springdoc-openapi-ui 1.5.2 

Ensuite, lorsque nous exécuterons notre application, les descriptions OpenAPI seront disponibles par défaut dans le chemin / v3 / api-docs - par exemple:

//localhost:8080/v3/api-docs/

Pour utiliser un chemin personnalisé, nous pouvons indiquer dans le fichier application.properties :

springdoc.api-docs.path=/api-docs

Maintenant, nous pourrons accéder aux documents à l'adresse:

//localhost:8080/api-docs/

Les définitions OpenAPI sont en JSONformat par défaut. Pour le format yaml , nous pouvons obtenir les définitions sur:

//localhost:8080/api-docs.yaml

3. Configuration de springdoc-openapi avec l'interface utilisateur Swagger

En plus de générer la spécification OpenAPI 3 elle-même, nous pouvons intégrer springdoc-openapi à Swagger UI afin de pouvoir interagir avec notre spécification API et exercer les points de terminaison.

3.1. Dépendance de Maven

Tout ce que nous avons à faire pour configurer springdoc-openapi avec Swagger UI est d'ajouter la dépendance springdoc-openapi-ui au pom.xml du projet :

 org.springdoc springdoc-openapi-ui 1.5.2 

Nous pouvons désormais accéder à la documentation de l'API à l'adresse:

//localhost:8080/swagger-ui.html

3.2. Prise en charge des propriétés swagger-ui

Springdoc-openapi prend également en charge les propriétés swagger-ui. Ceux-ci peuvent être utilisés comme propriétés Spring Boot, avec le préfixe springdoc.swagger-ui .

Par exemple, personnalisons le chemin de notre documentation API. Nous pouvons le faire en modifiant notre application.properties pour inclure:

springdoc.swagger-ui.path=/swagger-ui-custom.html

Alors maintenant, notre documentation API sera disponible sur //localhost:8080/swagger-ui-custom.html .

Comme autre exemple, pour trier les chemins d'API dans l'ordre de leurs méthodes HTTP, nous pouvons ajouter:

springdoc.swagger-ui.operationsSorter=method

3.3. Exemple d'API

Supposons que notre application dispose d'un contrôleur pour gérer les livres :

@RestController @RequestMapping("/api/book") public class BookController { @Autowired private BookRepository repository; @GetMapping("/{id}") public Book findById(@PathVariable long id) { return repository.findById(id) .orElseThrow(() -> new BookNotFoundException()); } @GetMapping("/") public Collection findBooks() { return repository.getBooks(); } @PutMapping("/{id}") @ResponseStatus(HttpStatus.OK) public Book updateBook( @PathVariable("id") final String id, @RequestBody final Book book) { return book; } } 

Ensuite, lorsque nous exécutons notre application, nous pouvons consulter la documentation à l'adresse:

//localhost:8080/swagger-ui-custom.html

Explorons le point de terminaison / api / book et voyons les détails de sa demande et de sa réponse:

4. Intégration de springdoc-openapi à Spring WebFlux

Nous pouvons intégrer springdoc-openapi et Swagger UI dans un projet Spring WebFlux en ajoutant springdoc-openapi-webflux-ui :

 org.springdoc springdoc-openapi-webflux-ui 1.5.2 

Et, comme auparavant, la documentation sera accessible à l'adresse:

//localhost:8080/swagger-ui.html

Afin de personnaliser le chemin, là encore nous pourrions ajouter la propriété springdoc.swagger-ui.path dans notre application.properties .

5. Exposition des informations de pagination

Spring Data JPA s'intègre à Spring MVC de manière assez transparente. Un exemple de telles intégrations est le support Pageable :

@GetMapping("/filter") public Page filterBooks(Pageable pageable) { return repository.getBooks(pageable); }

Au début, nous pouvons nous attendre à ce que SpringDoc ajoute des paramètres de requête de page , de taille et de tri à la documentation générée. Cependant, par défaut, SpringDoc ne répond pas à cette attente. Afin de déverrouiller cette fonctionnalité, nous devons ajouter la dépendance springdoc-openapi-data-rest :

 org.springdoc springdoc-openapi-data-rest 1.5.2 

Maintenant, il ajoute les paramètres de requête attendus à la documentation:

6. Utilisation du plug-in Maven springdoc-openapi

La bibliothèque springdoc-openapi fournit un plugin Maven springdoc-openapi-maven-plugin pour générer des descriptions OpenAPI aux formats json et yaml .

The springdoc-openapi-maven-plugin plugin works with the spring-boot-maven plugin. Maven runs the openapi plugin during the integration-test phase.

Let's see how we can configure the plugin in our pom.xml:

 org.springframework.boot spring-boot-maven-plugin 2.3.3.RELEASE   pre-integration-test  start    post-integration-test  stop      org.springdoc springdoc-openapi-maven-plugin 0.2   integration-test  generate    

Also, we can configure the plugin to use custom values:

  .........   //localhost:8080/v3/api-docs openapi.json ${project.build.directory}   

Let's take a closer look at the parameters that we can configure for the plugin:

  • apiDocsUrl – URL where the docs can be accessed in JSON format, with a default of //localhost:8080/v3/api-docs
  • outputFileName – Name of the file where the definitions are stored, defaults to openapi.json
  • outputDir – Absolute path for the directory where the docs are stored – by default, ${project.build.directory}

7. Automatic Document Generation Using JSR-303 Bean Validation

When our model includes JSR-303 bean validation annotations, such as @NotNull, @NotBlank, @Size, @Min, and @Max, the springdoc-openapi library uses them to generate additional schema documentation for the corresponding constraints.

Let's see an example using our Book bean:

public class Book { private long id; @NotBlank @Size(min = 0, max = 20) private String title; @NotBlank @Size(min = 0, max = 30) private String author; }

Now, the documentation generated for the Book bean is a little more informative:

8. Generate Documentation Using @ControllerAdvice and @ResponseStatus

Using @ResponseStatus on methods in a @RestControllerAdvice class will automatically generate documentation for the response codes. In this @RestControllerAdvice class, the two methods are annotated with @ResponseStatus:

@RestControllerAdvice public class GlobalControllerExceptionHandler { @ExceptionHandler(ConversionFailedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseEntity handleConnversion(RuntimeException ex) { return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST); } @ExceptionHandler(BookNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ResponseEntity handleBookNotFound(RuntimeException ex) { return new ResponseEntity(ex.getMessage(), HttpStatus.NOT_FOUND); } }

As a result, we can now see the documentation for the response codes 400 and 404:

9. Generate Documentation Using @Operation and @ApiResponses

Next, let's see how we can add some description to our API using a couple of OpenAPI-specific annotations.

In order to do that, we'll annotate our controller's /api/book/{id} endpoint with @Operation and @ApiResponses:

@Operation(summary = "Get a book by its id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Found the book", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Book.class)) }), @ApiResponse(responseCode = "400", description = "Invalid id supplied", content = @Content), @ApiResponse(responseCode = "404", description = "Book not found", content = @Content) }) @GetMapping("/{id}") public Book findById(@Parameter(description = "id of book to be searched") @PathVariable long id) { return repository.findById(id).orElseThrow(() -> new BookNotFoundException()); }

And here's the effect:

As we can see, the text we added to @Operation is placed at the API operation level. Similarly, the description added to various @ApiResponse elements in the @ApiResponses container annotation is also visible here, adding meaning to our API responses.

Evidently, we do not get any schema for the responses 400 and 404 above. Because we defined an empty @Content for them, only their descriptions are displayed.

10. Kotlin Support

Since Spring Boot 2.x has first-class support for Kotlin, SpringDoc supports this JVM language out of the box for Boot 2.x applications.

To see this in action, we'll create a simple Foo API in Kotlin.

After the initial setup, we'll add a data class and a controller. We'll add them in a sub-package of our Boot App so that when it's run, it picks our FooController up along with the earlier BookController:

@Entity data class Foo( @Id val id: Long = 0, @NotBlank @Size(min = 0, max = 50) val name: String = "" ) @RestController @RequestMapping("/") class FooController() { val fooList: List = listOf(Foo(1, "one"), Foo(2, "two")) @Operation(summary = "Get all foos") @ApiResponses(value = [ ApiResponse(responseCode = "200", description = "Found Foos", content = [ (Content(mediaType = "application/json", array = ( ArraySchema(schema = Schema(implementation = Foo::class)))))]), ApiResponse(responseCode = "400", description = "Bad request", content = [Content()]), ApiResponse(responseCode = "404", description = "Did not find any Foos", content = [Content()])] ) @GetMapping("/foo") fun getAllFoos(): List = fooList }

Now when we hit our API documentation URL, we'll see the Foo API as well:

To enhance the support of Kotlin types, we can add this dependency:

 org.springdoc springdoc-openapi-kotlin

After that, our Foo schema will look more informative. Similar to as it did when we had added JSR-303 Bean Validation:

11. Conclusion

In this article, we learned to set up springdoc-openapi in our projects. Then, we saw how to integrate springdoc-openapi with the Swagger UI. We also saw how to do this with Spring Webflux projects.

Next, we used the springdoc-openapi Maven Plugin to generate OpenAPI definitions for our APIs, and we saw how to expose paging and sorting information from Spring Data. After that, we looked at how springdoc-openapi generates documentation automatically using JSR 303 bean validation annotations and the @ResponseStatus annotations in @ControllerAdvice class.

Then we learned how to add a description to our API using a few OpenAPI-specific annotations. Finally, we took a peek at OpenAPI's support of Kotlin.

The springdoc-openapi generates API documentation as per OpenAPI 3 specification. Moreover, it also handles the Swagger UI configuration for us, making API document generation a fairly simple task.

As always, the code is available over on GitHub.

REST bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE