1. Introdução ao Swagger
No artigo anterior, introduzimos o suporte da Spring Boot para RESTful. Neste artigo, continuamos discutindo esse tópico. No entanto, não discutiremos mais como a API RESTful é implementada, mas discutiremos a manutenção da documentação da API RESTful.
No trabalho diário, geralmente precisamos fornecer interfaces para o front-end (final da web, iOS, Android) ou terceiros. No momento, precisamos fornecer a eles uma documentação detalhada da API. Mas manter um documento detalhado não é uma tarefa fácil. Primeiro de tudo, escrever um documento detalhado é uma tarefa demorada e trabalhosa. Por outro lado, como o código e o documento são separados, é fácil causar inconsistências entre o documento e o código. Neste artigo, compartilharemos uma maneira de manter os documentos da API, ou seja, para gerar automaticamente documentos de API restruitos através da Swagger.
Então, o que é Swagger? Podemos ler diretamente a descrição oficial:
A API Toolingswagge mais popular do mundo é a maior estrutura de ferramentas de desenvolvedor de API do mundo para a especificação OpenAPI (OAS), permitindo o desenvolvimento em todo o ciclo de vida da API, desde design e documentação, até teste e implantação.
Esta passagem primeiro diz que o Swagger é a ferramenta de API mais popular do mundo, e o objetivo do Swagger é apoiar o desenvolvimento de todo o ciclo de vida da API, incluindo design, documentação, teste e implantação. Neste artigo, usaremos as funções de gerenciamento e teste de documentos da Swagger.
Depois de obter uma compreensão básica do papel da arrogância, vamos dar uma olhada em como usá -lo.
2. Integração de arrogância e inicialização da primavera
Etapa 1: Apresente o pacote JAR correspondente:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.6.0</version></dependency><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.6.0</version></dependency>
Etapa 2: Configuração básica de informações:
@Configuration @Enableswagger2public class swagger2config {@Bean public Docket CreaterRestapi () {Retorne new Docket (documentationType.swagger_2) .apiinfo (apiinfo (). .Paths (PathSelectors.Regex ("/Rest /.*")) .build (); } apiinfo private apiinfo () {retorna new apiinfobuilder () .title ("sistema de blog RESTful API") .Description ("Sistema de blog Restful API") .termsOfServiceurl ("http://127.0.0.1:8080/"). .construir(); }}A configuração básica é uma descrição de todo o documento da API e algumas configurações globais, que funcionam para todas as interfaces. Existem duas anotações envolvidas aqui:
@Configuration significa que esta é uma classe de configuração, anotação fornecida pelo JDK, e foi explicada no artigo anterior.
@Enable Função do Swagger2 é ativar as funções relacionadas a Swagger2.
Nesta classe de configuração, instantei um objeto de boletim, que inclui principalmente três aspectos da informação:
(1) As informações de descrição de toda a API, ou seja, as informações incluídas no objeto APIINFO, esta parte das informações serão exibidas na página.
(2) Especifique o nome do pacote para gerar o documento da API.
(3) Especifique o caminho para gerar a API. A API baseada em caminho pode suportar quatro modos, que podem ser usados para se referir ao seu código-fonte:
public class PathSelectors {private PathSelectors () {THROW NEW UNSUPPORTEDOPERATIONECCECTION (); } predicado estático público <string> qualquer () {return predicates.alwaystrue (); } predicado estático public <string> NONE () {return predicates.alwaysfalse (); } public estático predicado <string> regex (final string pathregex) {return novo predicado <string> () {public boolean Apply (string input) {return input.matches (pathregex); }}; } public estático predicado <string> Ant (string final Antpattern) {return novo predicado <string> () {public boolean Apply (string input) {Antpathmatcher Matcher = new AntPathMatcher (); return matcher.match (antpattern, entrada); }}; }}Como pode ser visto no código-fonte, o Swagger suporta quatro maneiras: geração de qualquer caminho, não geração de qualquer caminho e correspondência regular e correspondência de padrões de formigas. Você pode estar mais familiarizado com os três primeiros tipos, o último da correspondência de formigas. Se você não estiver familiarizado com a Ant, apenas ignore -o. Os três primeiros tipos devem ser suficientes para que todos usem no trabalho diário.
Com a configuração acima, podemos ver o efeito. Eu tenho uma classe ArticleRestController sob o pacote com.pandy.blog.rest. O código -fonte é o seguinte:
Inicie a bota da primavera e visite: http://127.0.0.1:8080/swagger-ui.html para ver os seguintes resultados:
Você pode ver nesta página que, exceto para a última interface /teste /{id}, as outras interfaces geram documentos correspondentes. Como a última interface não atende ao caminho que configuramos - "/rest/.*", nenhum documento é gerado.
Também podemos clicar para ver cada interface específica. Vamos pegar a interface "Post /Rest /Artigo" como exemplo:
Como você pode ver, o Swagger gera exemplos de resultados de retorno e parâmetros de solicitação para cada interface e pode acessar diretamente a interface através do "Experimente" abaixo. Em termos de interface, todos testa a interface. No geral, Swagger ainda é muito poderoso e a configuração é relativamente simples.
@RestControllerPublic Classe ArticleRestController {@AUTOWIRED PRIVENTES ARTICLEVICE ARTICLESVED; @RequestMapping (Value = "/Rest/Artigo", Method = post, Produces = "Application/JSON") public webSponse <map <string, object >> SaveArticle (artigo do artigo do @Requestbody) {Article.setUserID (1L); Articleservice.Savearticle (artigo); Mapa <string, object> ret = new hashmap <> (); ret.put ("id", artigo.getId ()); WebSponse <map <string, object >> resposta = webSponse.getSuccessResponse (RET); resposta de retorno; } @RequestMapping (value = "/Rest/Artigo/{id}", Method = delete, Produces = "Application/JSON") public webSponse <?> Deletearticle (@PathVariable Long Id) {Artigo = Articleservice.GetById (id); artigo.SetStatus (-1); Articleservice.UpDatearticle (Artigo); WebSponse <ject> Response = webSponse.getSuccessResponse (NULL); resposta de retorno; } @ReQuestMapping (value = "/Rest/Artigo/{id}", Method = put, Produces = "Application/JSON") public webrosponse <ject> updateArticle (@pathvariable longo id, artigo @requestbody artigo) {artigo.setId (id); Articleservice.UpDatearticle (Artigo); WebSponse <ject> Response = webSponse.getSuccessResponse (NULL); resposta de retorno; } @RequestMapping (value = "/Rest/Artigo/{id}", Method = get, Produces = "Application/JSON") public webSponse <Artigo> getArticle (@PathVariable Long Id) {Artigo = Articleservice.getById (id); WebResponse <Artigo> Response = webSponse.getSuccessResponse (Artigo); resposta de retorno; } @RequestMapping (value = "/test/{id}", método = get, produz = "Application/json") public webSponse <? resposta de retorno; }}3. Configuração detalhada da API de swagger
Mas você definitivamente terá algumas perguntas quando vir isso:
A primeira pergunta: não há descrição literal do resultado de retorno e dos parâmetros de solicitação. Isso pode ser configurado?
A segunda pergunta: este parâmetro de solicitação deve ser refletido diretamente com base no objeto, mas nem todas as propriedades do objeto são necessárias e o valor do parâmetro pode não atender às nossas necessidades. Isso pode ser configurado?
A resposta é certamente ok. Agora vamos resolver esses dois problemas e olhar diretamente para o código de configuração:
pacote com.pandy.blog.rest; importar com.pandy.blog.dto.webesponse; importar com.pandy.blog.po.article; importar com.pandy.blog.service.ticleservice; importação io.swagger.annotations.apiimplicParam; io.swagger.annotações.apioperation; importar io.swagger.annotações.apiresponse; importar io.swagger.annotações.apiresponse; importar io.swagger.annotações.apiresponse; import ioo.swagger.annotações.apiresponse; importação.seiringframewan.banky.bagory.annotações.apiresponse; org.springframework.context.annotation.Profile;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.restcontroller; importar java.util.hashmap; importar java.util.list; importar java.util.map; importar org.springframework.web.bind.annotation.requestmethod.detlet.DelEste; org.springframework.web.bind.annotation.requestmethod.get; importar org.springframework.web.bind.annotation.requestmethod.post; importmethodsorg.springframework.web.bind.annotation.requestmethods.Static.StringFramework.WebConRmmmmetrmetModsMod.PertMod.PringFramework.web.bind.annation.requestmethods.Static.StringFramework.WebConRmmmmetrmetModsMod.PertMod.PringFramework.WebConRmetMmmetrmetMod.PertMod.PertMod.PringFramework.web.Bind.anationAptation.requestmethods.Tatic.Stration; ArticleRestController {@Autowired Private Articleservice Articleservice; @RequestMapping (value = "/artigo", método = post, produz = "Application/json") @Apioperation (value = "Adicione artigo", Notes = "Adicione novo artigo", Tags = "Artigo", HttpMethod = "Post") @apiImplicticParams (@ApiImplicitParam (Nome = "") @ApiimplicicParam (name = "resumo", value = "resumo do artigo", requerido = true, datatype = "string"), @apiimplicitParam (name = "status", value = "publicish status", requerir = true, datatype = "integer")}) @apirESPonses ({@apirSonsonse (code = 200, 200, serpirSonseSoST), "@ApSoSoST)," @AsterSoST), "@AsterSoST), (@AsterSoSt),", resmusta). WebResponse <map <string, object >> savearticle (artigo do @requestbody artigo) {Articleservice.SaveArticle (artigo); Mapa <string, object> ret = new hashmap <> (); ret.put ("id", artigo.getId ()); WebSponse <map <string, object >> resposta = webSponse.getSuccessResponse (RET); resposta de retorno; } @Apioperation (value = "Excluir artigo", Notes = "Excluir artigo por id", tags = "Artigo", httpmethod = "delete") @apiimplicitParams ({@apiimplicticParam (name = "id", value = "artigo", exigido = true, datatype = "long"). Delete, Produces = "Application/JSON") Public WebSponse <?> Deletearticle (@PathVariable Long Id) {Artigo = ArticleService.GetById (ID); artigo.SetStatus (-1); Articleservice.Savearticle (artigo); return webroponse.getSuccessResponse (novo hashmap <> ()); } @Apioperation (value = "Obtenha a lista de artigos", Notes = "Consulta completa de acordo com o título", tags = "Artigo", httpmethod = "get") @apiimplicticParams ({@apiimplicitParam (name = "title", valor "," title ", requery = false = datatype =" stringam), "title articles per page", required = false, dataType = "Integer"), @ApiImplicitParam(name = "pageNum", value = "PagePage Number", required = false, dataType = "Integer") }) @RequestMapping(value = "/article/list", method = GET, produces = "application/json") public WebResponse<?> listArticles(String title, Integer pageSize, Número inteiro pagenum) {if (PageSize == NULL) {Pagesize = 10; } if (pagenum == null) {pagenum = 1; } int offset = (Pagenum - 1) * PageSize; Lista <Artigo> Articles = ArticleService.Getarticles (título, 1L, Offset, PageSize); retornar webrosponse.getSuccessResponse (artigos); } @Apioperation (value = "Atualizar artigo", Notes = "Atualizar conteúdo do artigo", tags = "Artigo", httpmethod = "put") @apiimplicticParams ({@apiimplicticParam (name = "id", value = "artigo", requerir = datatype = "long"), @APiImiMIm, = "String"), @ApiImplicitParam(name = "summary", value = "Article summary", required = false, dataType = "String"), @ApiImplicitParam(name = "status", value = "Publish status", required = false, dataType = "Integer") }) @RequestMapping(value = "/article/{id}", method = PUT, produces = "application/json") public WebResponse <?> Updatearticle (@pathvariable longo id,@requestbody artigo do corpo) {article.setId (id); Articleservice.UpDatearticle (Artigo); return webroponse.getSuccessResponse (novo hashmap <> ()); }}Vamos explicar as funções específicas de várias anotações e atributos relacionados no código:
@Apioperation, toda a configuração de atributo da interface:
Valor: Descrição da interface, exibida na lista de interface.
Notas: Descrição detalhada da interface, exibida na página Detalhes da interface.
Tags: a tag da interface. A interface com a mesma tag será exibida na página de uma guia.
httpmethod: método http suportado.
@Apiimplicticparams, um contêiner para @apiimplicticparam, pode conter várias anotações @apiimplicticparam
@APIImplicticParam, solicitar o parâmetro Atributo Configuração:
Nome: Nome do parâmetro
Valor: Descrição do parâmetro
Necessário: é necessário
Datatype: Tipo de dados
@APIRESPONSES, @APIResponse Container, pode conter várias anotações @apiresponse
@Apiresponse, retorne o resultado da configuração do atributo:
Código: Retorna a codificação do resultado.
Mensagem: Retorna a descrição do resultado.
Resposta: Retorna a classe correspondente do resultado.
Depois de concluir a configuração acima, vejamos o efeito da página:
Página de lista:
Como você pode ver, as interfaces agora estão localizadas na tag do artigo e também existem instruções para nossa configuração por trás da interface. Vejamos a página de detalhes da interface "postagem /descanso /artigo":
A imagem é muito grande e apenas a exibição do atributo de título é interceptada e os outros parâmetros são semelhantes. Podemos ver na página que existem instruções para parâmetros de solicitação, mas esse não é o efeito que esperávamos. Se nossos parâmetros são apenas tipos simples, esse método deve ser bom, mas o problema agora é que nossos parâmetros de solicitação são um objeto, então como configurá -los? Isso envolve duas outras anotações: @apimodel e @apimodelproperty. Vejamos o código primeiro e depois explicá -lo, o que é mais fácil de entender:
@Apimodel (value = "objeto do artigo", description = "Adicionar e atualizar artigo Descrição do objeto") Artigo da classe public {@id @generatedValue @apimodelProperty (name = "id", value = "artigo ID", requerido = false, exemplo = "1") Private Long Id; @ApimodelProperty (name = "title", value = "título do artigo", requerir = true, exemplo = "Título do artigo do teste") Título da String Private; @ApimodelProperty (name = "resumo", value = "resumo do artigo", requerido = true, exemplo = "resumo do artigo do teste") Resumo da sequência privada; @Apimodelproperty (hidden = true) data privada createTime; @ApimodelProperty (hidden = true) Data privada Publictime; @Apimodelproperty (hidden = true) data privada updateTime; @ApimodelProperty (hidden = true) private UserID Long; @ApimodelProperty (name = "status", value = "status da liberação do artigo", requerir = true, exemplo = "1") status inteiro privado; @ApimodelProperty (name = "type", value = "categoria de artigo", requerir = true, exemplo = "1") Tipo inteiro privado;}@Apimodel é a configuração das propriedades de toda a classe:
Valor: Descrição da classe
Descrição: Descrição detalhada
@ApimodelProperty é a configuração das propriedades de cada campo em detalhes:
Nome: Nome do campo
Valor: Descrição do campo
Necessário: é necessário
Exemplo: Valor de exemplo
oculto: se deve exibir
Depois de concluir a configuração acima, vejamos o efeito:
Agora, podemos ver que a descrição do campo foi mostrada e o valor do campo no exemplo também se tornou o valor correspondente da propriedade Exemplo que configuramos. Dessa forma, um documento completo da API é gerado e o documento está intimamente vinculado ao código, em vez das duas peças isoladas. Além disso, também podemos testá -lo facilmente através deste documento. Só precisamos clicar na caixa amarela em valor de exemplo, e o conteúdo interno será copiado automaticamente para a caixa de valor correspondente do artigo e, em seguida, clique em "Experimente" para iniciar uma solicitação HTTP.
Depois de clicar em experimentar, podemos ver o resultado retornado:
A operação ainda é muito conveniente. Comparado com Junit e Postman, os testes através do Swagger são mais convenientes. Obviamente, os testes de Swagger não podem substituir os testes de unidade, mas ainda tem um efeito muito importante na depuração conjunta.
4. Resumo
No geral, a configuração de Swagger é relativamente simples, e a capacidade da Swagger de gerar documentos automaticamente realmente nos salvou muito trabalho e deu grande ajuda à manutenção subsequente. Além disso, o Swagger pode gerar automaticamente dados de teste para nós de acordo com a configuração e fornecer métodos HTTP correspondentes, o que também é útil para o nosso autoteste e o trabalho de depuração conjunta. Portanto, eu ainda recomendo que você use o Swagger no desenvolvimento diário, o que deve ajudá -lo a melhorar sua eficiência de trabalho até certo ponto. Por fim, deixe -me deixar uma pergunta para você pensar, ou seja, o documento pode ser acessado diretamente através da página, para que não possamos expor a interface diretamente ao ambiente de produção, especialmente os sistemas que precisam fornecer serviços externos. Então, como podemos desativar essa função no processo de produção? Existem muitos métodos, você pode experimentar você mesmo.
O exposto acima é o combate real do projeto Swagger2 integrado de bota de primavera introduzida a você pelo editor. Espero que seja útil para você. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a você a tempo. Muito obrigado pelo seu apoio ao site wulin.com!