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)