DEV Community

Fabiano Góes • e-Programar
Fabiano Góes • e-Programar

Posted on

Documentando uma API REST Spring Boot 3 usando OpenAPI 3.0

Visão Geral

A documentação é uma parte essencial da construção de APIs REST. Neste tutorial, veremos SpringDoc, que simplifica a geração e manutenção de documentos de API com base na especificação OpenAPI 3 para aplicativos Spring Boot 3.x.

Configuração

Image description

Spring Boot 3.x requer o uso da versão 2 do springdoc-openapi:

 ext { springdocVersion = "2.3.0" } dependencies { implementation "org.springframework.boot:spring-boot-starter-web" // ... implementation "org.springframework.boot:spring-boot-starter-validation" implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${springdocVersion}" implementation "org.springdoc:springdoc-openapi-starter-common:${springdocVersion}" // ... testImplementation "org.springframework.boot:spring-boot-starter-test" } 
Enter fullscreen mode Exit fullscreen mode

Depois de configurar a dependência corretamente, podemos executar nossa aplicação e encontrar as descrições da OpenAPI em /v3/api-docs, que é o caminho padrão:

 http://localhost:8080/v3/api-docs 
Enter fullscreen mode Exit fullscreen mode

Podemos personalizar o caminho em application.properties usando a propriedade springdoc.api-docs. Por exemplo, podemos definir o caminho para /api-docs:

 springdoc.api-docs.path=/api-docs 
Enter fullscreen mode Exit fullscreen mode

Então, poderemos acessar a documentação na url:

 http://localhost:8080/api-docs 
Enter fullscreen mode Exit fullscreen mode

As definições OpenAPI estão no formato JSON por padrão. Para o obter o formato yaml, podemos podemos acessar a url:

 http://localhost:8080/api-docs.yaml 
Enter fullscreen mode Exit fullscreen mode

Integração com UI Swagger

Além de gerar a especificação OpenAPI 3, podemos integrar springdoc-openapi com Swagger UI para interagir com nossa especificação de API e testar os endpoints. A dependência springdoc-openapi já inclui a UI do Swagger, então neste ponto, já podemos acessar a Swagger UI em:

 http://localhost:8080/swagger-ui/index.html 
Enter fullscreen mode Exit fullscreen mode

Support for swagger-ui Properties

A biblioteca springdoc-openapi também suporta propriedades swagger-ui. Elas podem ser usados ​​como propriedades Spring Boot com o prefixo springdoc.swagger-ui. Por exemplo, podemos personalizar o caminho da Swagger UI alterando a propriedade springdoc.swagger-ui.path dentro do nosso arquivo application.properties:

 springdoc.swagger-ui.path=/documentation.html 
Enter fullscreen mode Exit fullscreen mode

Então podemos acessar a Swagger UI na url:

 http://localhost:8080/documentation.html 
Enter fullscreen mode Exit fullscreen mode

API de exemplo

 import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriComponentsBuilder; import java.util.Collection; @RestController @RequestMapping("/api/books") public class BookController { private final BookRepository repository; public BookController(BookRepository repository) { this.repository = repository; } @PostMapping public ResponseEntity<?> create(@RequestBody final Book payload, UriComponentsBuilder ucb) { Book book = repository.create(payload); var location = ucb .path("/movies/{id}") .buildAndExpand(book.getId()) .toUri(); return ResponseEntity.created(location).build(); } @GetMapping("/{id}") public Book findById(@PathVariable final Long id) { return repository.findById(id) .orElseThrow(BookNotFoundException::new); } @GetMapping("/") public Collection<Book> findBooks() { return repository.getBooks(); } @PutMapping("/{id}") @ResponseStatus(HttpStatus.OK) public ResponseEntity<?> update(@PathVariable("id") final Long id, @RequestBody final Book payload) { if (repository.findById(id).isEmpty()) return ResponseEntity.notFound().build(); return ResponseEntity.accepted().body(repository.update(id, payload)); } @DeleteMapping("/{id}") public ResponseEntity<?> delete(@PathVariable final Long id) { Book book = repository.findById(id).orElseThrow(BookNotFoundException::new); repository.delete(book); return ResponseEntity.noContent().build(); } } 
Enter fullscreen mode Exit fullscreen mode

Repositório de exemplo

 import org.springframework.stereotype.Repository; import java.util.*; @Repository public class BookRepository { private final Map<Long, Book> books = new HashMap<>(); public Optional<Book> findById(final long id) { return Optional.ofNullable(books.get(id)); } public Book create(Book book) { Long id = (long) (books.size() + 1); book.setId(id); books.put(id, book); return books.get(id); } public Collection<Book> getBooks() { return books.values(); } public Book update(final long id, final Book book) { Book bookUpdate = books.get(id); bookUpdate.setAuthor(book.getAuthor()); bookUpdate.setTitle(book.getTitle()); books.put(id, bookUpdate); return books.get(id); } public void delete(final Book book) { books.remove(book.getId()); } } 
Enter fullscreen mode Exit fullscreen mode

Modelo de exemplo

 import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; public class Book { private long id; private String title; private String author; // Getters and Setters } class BookNotFoundException extends RuntimeException { } 
Enter fullscreen mode Exit fullscreen mode

Application Properties de exemplo

 # Open Api springdoc.api-docs.path=/api-docs springdoc.swagger-ui.operationsSorter=alpha springdoc.api-docs.version=OPENAPI_3_0 spring.jpa.hibernate.ddl-auto=none # Swagger springdoc.swagger-ui.path=/documentation.html 
Enter fullscreen mode Exit fullscreen mode

Testando

Então, se executarmos nossa API agora, podemos visualizar a documentação em:

 http://localhost:8080/documentation.html 
Enter fullscreen mode Exit fullscreen mode

Image description

Aqui podemos usar a Swagger UI para testar nossa API fazendo:

  • cadastro de novos livros
  • obtendo todos os livros cadastrados
  • obtendo um livro pelo id
  • atualizando um livro cadastrado
  • deletando um livro cadastrado

Faça testes e explore a Documentação gerada automáticamente.

Geração automática de documentos usando validação de bean JSR-303

Quando nosso modelo inclui anotações de validação de bean JSR-303, como @NotNull, @NotBlank, @Size, @Min e @Max, a biblioteca springdoc-openapi utiliza essas anotações para gerar documentação de esquema adicional para as restrições correspondentes. Vejamos um exemplo usando nosso bean Book:

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

Percebe agora como ficou a documentação do Schema de Book:

Image description

Gerando documentação adicional usando @ControllerAdvice e @ResponseStatus:

Usar @ResponseStatus em métodos em uma classe @RestControllerAdvice o openapi vai gerar automaticamente documentação para os códigos de resposta. Nesta classe @RestControllerAdvice, os dois métodos são anotados com @ResponseStatus:

 import org.springframework.core.convert.ConversionFailedException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalControllerExceptionHandler { @ExceptionHandler(ConversionFailedException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseEntity<String> handleConversion(RuntimeException ex) { return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); } @ExceptionHandler(BookNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ResponseEntity<String> handleBookNotFound(RuntimeException ex) { return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND); } } 
Enter fullscreen mode Exit fullscreen mode

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

Image description

Gerando documentação usando @Operation e @ApiResponses

A seguir, vamos ver como podemos adicionar alguma descrição à nossa API usando algumas anotações da especificação OpenAPI.

Para fazer isso, anotaremos o endpoint /api/book/{id} do nosso controlador com @Operation e @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()); } 
Enter fullscreen mode Exit fullscreen mode

Agora nosso endpoint /api/book/{id} ficou documentado assim:

Image description

Conclusão

Neste artigo, aprendemos como configurar o springdoc-openapi em nossos projetos. Em seguida, vimos como integrar springdoc-openapi com a UI do Swagger.

Depois disso, vimos como o springdoc-openapi gera documentação automaticamente usando anotações de validação de bean JSR 303 e as anotações @ResponseStatus na classe @ControllerAdvice.

Também aprendemos como adicionar uma descrição à nossa API usando algumas anotações específicas da OpenAPI

O springdoc-openapi gera documentação da API de acordo com as especificações do OpenAPI 3. Além disso, ele também cuida da configuração da UI do Swagger para nós, tornando a geração de documentos da API uma tarefa razoavelmente simples.

Referencias

Top comments (0)