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
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"
}
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
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
Então, poderemos acessar a documentação na url:
http://localhost:8080/api-docs
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
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
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
Então podemos acessar a Swagger UI na url:
http://localhost:8080/documentation.html
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();
}
}
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());
}
}
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 {
}
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
Testando
Então, se executarmos nossa API agora, podemos visualizar a documentação em:
http://localhost:8080/documentation.html
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;
}
Percebe agora como ficou a documentação do Schema de Book:
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);
}
}
As a result, we can now see the documentation for the response codes 400
and 404
:
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());
}
Agora nosso endpoint /api/book/{id}
ficou documentado assim:
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.
Top comments (0)