Documentar uma aplicação é um ponto essencial de qualquer projeto, muitas vezes negligenciado. Quando se trabalha em equipe, uma má documentação pode dificultar (e muito) o trabalho dos demais desenvolvedores.
Por já ter muita dificuldade com isso, procuro sempre documentar as aplicações que estou trabalhando. No ano passado, mostrei como documentar uma aplicação ASP.NET Core Web API com o Swagger. Por gostar muito dele, nesta semana quando precisei documentar API Spring Boot, não tive dúvidas que ele é era a escolha correta.
A partir da experiência que tive neste processo, aqui vou lhe mostrar como documentar uma API Spring Boot com o Swagger.
Relembrando características do Swagger
Caso não tenha visto o meu artigo de ASP.NET Core Web API com o Swagger, vamos relembrar alguns detalhes do Swagger:
- O Swagger é uma aplicação open source que auxilia os desenvolvedores a definir, criar, documentar e consumir APIs REST;
- É composto de um arquivo de configuração, que pode ser definido em YAML ou JSON;
- Fornece ferramentas para: auxiliar na definição do arquivo de configuração (Swagger Editor), interagir com API através das definições do arquivo de configuração (Swagger UI) e gerar templates de código a partir do arquivo de configuração (Swagger Codegen).
Como é possível notar, o ponto mais importante é o arquivo de configuração do Swagger. É nele que a API é documentada.
Criar este arquivo na mão pode ser um trabalho hercúleo, felizmente existem algumas bibliotecas do Java que facilitam este processo. No caso de uma aplicação Spring, a melhor opção é a biblioteca SpringFox.
Curso Java - Fundamentos de Struts 2
Conhecer o cursoAdicionando a biblioteca SpringFox em uma aplicação API Spring Boot
Para este artigo, estou utilizando a aplicação mostrada no meu último artigo, onde ensinei a criar uma aplicação REST API no Spring Boot. Você também pode ver a aplicação que criei no meu Github.
Com a aplicação criada, temos que adicionar a dependência do SpringFox nela:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
Para interagir com a configuração, também é necessário adicionar a dependência que fornece o Swagger UI:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Agora para que o arquivo de especificação da API seja criado, é necessário habilitar o Swagger na aplicação. Para isso, adicione nela uma classe chamada SwaggerConfig
, com o conteúdo abaixo:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
No Spring Boot o Swagger é ativado através da anotação @EnableSwagger2
. O Docket
que estamos definindo no nosso bean nos permite configurar aspectos dos endpoints expostos por ele.
Nos métodos apis()
e paths()
definimos que todas as apis e caminhos estarão disponíveis. Com isso através de reflection a biblioteca já consegue obter os endpoints definidos na aplicação.
Ao executá-la, o Swagger UI estará disponível em /swagger-ui.html
:
Note que ele está pegando informações de todos os controllers definidos na aplicação até o de erro padrão do Spring Boot. Caso queria evitar isso, é possível configurar o Swagger.
Customizando o Swagger na aplicação Spring Boot
Todas as configurações do Swagger devem ser definidas na classe SwaggerConfig
. No momento ela contém apenas as configurações padrão.
Indicando código e mensagem de retorno do Swagger
Nas configurações padrão, o Swagger irá indicar que os endpoints retornam os códigos 200, 201, 204, 401, 403 e 404. Caso a sua aplicação não retorne todos esses códigos, você pode especificar quais códigos ela retorna com o método globalResponseMessage
.
O globalResponseMessage
recebe por parâmetro o método HTTP e uma lista de ResponseMessage
que indica quais códigos e mensagens o método retorna. Para que a aplicação fique modular, vamos definir os ResponseMessage
em um método:
private List<ResponseMessage> responseMessageForGET()
{
return new ArrayList<ResponseMessage>() {{
add(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build());
add(new ResponseMessageBuilder()
.code(403)
.message("Forbidden!")
.build());
}};
}
E ele será passado como parâmetro do globalResponseMessage
:
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}
Agora, ficará indicado que todos os endpoints GET retornam o código 200 (que é sempre padrão) e os códigos 500 e 403:
Note na imagem acima, que em “Response content type” é indicado o valor /. Para alterar este ponto, basta indicar no seu endpoint o tipo do conteúdo que ele produz com o atributo produces
:
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
return _pessoaRepository.findAll();
}
Com o atributo consumes
é possível especificar o tipo de conteúdo que ele consome:
@RequestMapping(value = "/pessoa", method = RequestMethod.POST, produces="application/json", consumes="application/json")
public Pessoa Post(@Valid @RequestBody Pessoa pessoa)
{
return _pessoaRepository.save(pessoa);
}
Também é possível especificar os códigos e as mensagens de retorno diretamente no controller com as anotações @ApiResponses
e @ApiResponse
:
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
@ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
@ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
return _pessoaRepository.findAll();
}
Estas configurações serão válidas para os endpoints onde estiverem definidos. Onde não estiver, será utilizado o padrão.
Também é possível utilizar a anotação @ApiOperation
para descrever o endpoint:
@ApiOperation(value = "Retorna uma lista de pessoas")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Retorna a lista de pessoa"),
@ApiResponse(code = 403, message = "Você não tem permissão para acessar este recurso"),
@ApiResponse(code = 500, message = "Foi gerada uma exceção"),
})
@RequestMapping(value = "/pessoa", method = RequestMethod.GET, produces="application/json")
public List<Pessoa> Get() {
return _pessoaRepository.findAll();
}
E caso queira descrever o model, pode ser utilizado a anotação @ApiModelProperty
:
@Entity
public class Pessoa
{
@ApiModelProperty(value = "Código da pessoa")
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ApiModelProperty(value = "Nome da pessoa")
@Column(nullable = false)
private String nome;
public long getId() {
return id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public void setId(long id) {
this.id = id;
}
}
Curso Java - Introdução à JPA
Conhecer o cursoFiltrando os endpoints da API Spring Boot no Swagger
No momento o Swagger está listando todos os endpoints definidos na aplicação, inclusive os padrões do Spring Boot, como o de erro. Mas na configuração dele, no método apis
podemos utilizar a classe RequestHandlerSelectors
para filtrar quais serão considerados com base no pacote ou anotação. Por exemplo, para que seja listada apenas os endpoints definidos pela nossa aplicação, utilizamos o método basePackage()
desta classe:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}
Com a classe PathSelectors
, também é possível filtrar os caminhos aceitos para os endpoints, como:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
.paths(PathSelectors.ant("/api/*"))
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, responseMessageForGET());
}
Acima estamos utilizando o método ant()
para adicionar o filtro, mas também seria possível definir uma expressão regular com o método regex()
.
Especificando a autenticação da aplicação no Swagger
Uma das principais vantagens da Swagger UI é a possibilidade de testar os endpoints diretamente pela interface. Quando a aplicação define alguma autenticação, é necessário configurar isso, para que o SpringFox também especifique isso, e mesmo endpoints protegidos sejam testáveis.
Esta especificação é realizada com os métodos securitySchemes
e securityContexts
. No primeiro é definido o tipo de autenticação (no momento os suportados são: ApiKey, BasicAuth e OAuth). Já no segundo são especificadas particularidades desta autenticação, como os escopos e endpoints que necessitam de autenticação.
No caso deste exemplo, será definida uma autenticação ApiKey:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, responseMessageForGET())
.securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
.securityContexts(Arrays.asList(securityContext()));
}
Já o securityContext()
conterá o código abaixo:
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.ant("/pessoa/**"))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("ADMIN", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(
new SecurityReference("Token Access", authorizationScopes));
}
Onde é definido que os endpoint de /pessoa
necessitam de autenticação.
Agora ao executar a aplicação e acessar o Swagger, haverá o botão “Authorize”:
Ao clicar nele, será exibido a tela onde a forma de acesso deve ser informada:
Na aplicação deste exemplo, a autenticação é TOKEN, então ao informá-lo e clicar em Authorize, será possível realizar solicitações nos endpoints que necessitam de autenticação.
Descrevendo a API no Swagger
Por fim, é possível adicionar algumas informações da api no método apiInfo
:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("br.com.treinaweb.springbootapi.controller"))
.paths(PathSelectors.any())
.build()
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET, responseMessageForGET())
.securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name())))
.securityContexts(Arrays.asList(securityContext()))
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Simple Spring Boot REST API")
.description(""Um exemplo de aplicação Spring Boot REST API"")
.version("1.0.0")
.license("Apache License Version 2.0")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0"")
.contact(new Contact("Wladimilson", "https://treinaweb.com.br", "contato@treinaweb.com.br"))
.build();
}
O resultado será:
Você pode ver a aplicação deste artigo no meu Github.
Devo usar o Swagger em uma aplicação API Spring Boot?
Neste artigo vemos que está claro que o Swagger é uma ótima forma de documentar APIs REST e com a biblioteca SpringFox, caso esteja desenvolvendo uma API Spring Boot, a geração da especificação do Swagger é facilitada. Assim, sempre que possível documente as suas aplicações utilizando esta ferramenta. Tenho certeza que isso irá facilitar o trabalho de todos que consomem a sua API, incluindo você.