1. Introduction à Swagger
Dans l'article précédent, nous avons introduit la prise en charge de Spring Boot pour Restful. Dans cet article, nous continuons à discuter de ce sujet. Cependant, nous ne discuterons plus de la façon dont l'API RESTful est mise en œuvre, mais nous discuterons plutôt de la maintenance de la documentation API RESTFul.
Dans les travaux quotidiens, nous devons souvent fournir des interfaces à l'avant (Web End, iOS, Android) ou à des tiers. Pour le moment, nous devons leur fournir une documentation API détaillée. Mais le maintien d'un document détaillé n'est pas une tâche facile. Tout d'abord, la rédaction d'un document détaillé est une tâche longue et laborieuse. D'un autre côté, puisque le code et le document sont séparés, il est facile de provoquer des incohérences entre le document et le code. Dans cet article, nous partagerons un moyen de maintenir les documents d'API, c'est-à-dire pour générer automatiquement des documents API réparateurs via Swagger.
Alors qu'est-ce que Swagger? Nous pouvons lire directement la description officielle:
Le plus populaire API les plus populaires au monde est le plus grand cadre mondial d'outils de développeur API pour la spécification OpenAPI (OEA), permettent le développement de tout le cycle de vie de l'API, de la conception et de la documentation, pour tester et déploier.
Ce passage vous dit d'abord que Swagger est l'outil API le plus populaire au monde, et le but de Swagger est de soutenir le développement de l'ensemble du cycle de vie de l'API, notamment la conception, la documentation, les tests et le déploiement. Dans cet article, nous utiliserons les fonctions de gestion et de test des documents de Swagger.
Après avoir acquis une compréhension de base du rôle de Swagger, jetons un coup d'œil à l'utiliser.
2. Intégration de la botte Swagger et Spring
Étape 1: Présentez le package JAR correspondant:
<Dedency> <GroupId> io.springfox </proupId> <Artifactid> Springfox-Swagger2 </letefactId> <Dersion> 2.6.0 </prewing> </ Dependency> <Dependency> <ProupID> io.springfox </prowprid> <Artifactid> SpringFox-Swagger-Ui </ ArtifActid> <fridepree> <Dudition> *.
Étape 2: Configuration des informations de base:
@ Configuration @ ActivedWagger2Public class swagger2config {@bean public Docket Createstapi () {return new Docket (documentationType.swagger_2) .apiinfo (apiinfo ()) .select () .apis (requestLerselector .Paths (pathSelectors.regex ("/ rest /.*")) .build (); } private apiinfo apiinfo () {return new apiInfobuilder () .Title ("Blog System Resful API") .Description ("Blog System Resful API") .termsofServiceUrl ("http://127.0.0.1:8080/") .Contact ("liuxiaopeng"). .construire(); }}La configuration de base est une description de l'ensemble du document API et de certaines configurations globales, qui fonctionnent pour toutes les interfaces. Il y a deux annotations impliquées ici:
@Configuration signifie qu'il s'agit d'une classe de configuration, d'annotation fournie par JDK, et a été expliquée dans l'article précédent.
La fonction de @ activewagger2 est d'activer les fonctions liées à Swagger2.
Dans cette classe de configuration, j'ai instancié un objet de dossier, qui comprend principalement trois aspects de l'information:
(1) Les informations de description de l'API entière, c'est-à-dire les informations incluses dans l'objet APIInfo, cette partie des informations sera affichée sur la page.
(2) Spécifiez le nom du package pour générer le document API.
(3) Spécifiez le chemin pour générer l'API. L'API basée sur le chemin peut prendre en charge quatre modes, qui peuvent être utilisés pour désigner son code source:
classe publique PathSelectors {private PathSelectors () {lancez un nouveau UnsupporttedOperationException (); } prédicat statique public <string> any () {return prédicats.alwaystrue (); } prédicat statique public <string> Aucun () {return prédicats.alwaysfalse (); } Public Static Predicat <string> regex (final String pathRegex) {return new Predicat <string> () {public boolean applique (string input) {return input.matches (pathRegex); }}; } Public Static Predicat <string> ant (final String antpattern) {return new Predicat <string> () {public boolean applique (entrée de chaîne) {antpathmatcher Matcher = new antpathmatcher (); return Matcher.match (antpattern, entrée); }}; }}Comme on peut le voir à partir du code source, Swagger prend en charge quatre façons: génération de n'importe quel chemin, non-générateur de n'importe quel chemin et correspondance régulière et appariement des motifs de fourmis. Vous êtes peut-être plus familier avec les trois premiers types, le dernier de la correspondance des fourmis. Si vous n'êtes pas familier avec Ant, ignorez-le. Les trois premiers types devraient être suffisants pour que tout le monde puisse utiliser dans le travail quotidien.
Avec la configuration ci-dessus, nous pouvons voir l'effet. J'ai une classe ActivlerestController sous le package com.pandy.blog.rest. Le code source est le suivant:
Commencez Spring Boot, puis visitez: http://127.0.0.1:8080/swagger-ui.html pour voir les résultats suivants:
Vous pouvez voir sur cette page que, à l'exception de la dernière interface / test / {id}, les autres interfaces génèrent des documents correspondants. Parce que la dernière interface ne répond pas au chemin que nous avons configuré - "/rest/.*", aucun document n'est généré.
Nous pouvons également cliquer sur pour voir chaque interface spécifique. Prenons l'exemple de l'interface «post / repos / article»:
Comme vous pouvez le voir, Swagger génère des exemples de résultats de retour et de paramètres de demande pour chaque interface, et peut accéder directement à l'interface via le "essayez-le" ci-dessous. En termes d'interface, tout le monde teste l'interface. Dans l'ensemble, Swagger est toujours très puissant et la configuration est relativement simple.
@RestControllerPublic Class ARTICLERESTCTORLER {@Autowired Private ArticleService ArticleService; @RequestMapping (value = "/ rest / article", méthode = post, produit = "application / json") public webResponse <map <string, object >> savearticle (@requestbody article article) {article.SetUserId (1L); ArticleService.Savearticule (article); Map <string, objet> ret = new Hashmap <> (); ret.put ("id", article.getId ()); WeBResponse <map <string, objet >> réponse = weBResponse.getSuccessResponse (ret); réponse de retour; } @RequestMapping (value = "/ rest / article / {id}", méthode = delete, produmes = "application / json") public webrePonse <?> Deletearticle (@pathvariable long id) {article Article = ArticleService.getById (id); Article.SetStatus (-1); ArticleService.UpdateAralicle (article); WeBResponse <Bject> Response = weBResponse.getSuccessResponse (null); réponse de retour; } @RequestMapping (value = "/ rest / article / {id}", méthode = put, produmes = "application / json") public webResponse <Object> updateArticle (@Pathvariable long id, @Requestbody Article Article) {Article.SetId (id); ArticleService.UpdateAralicle (article); WeBResponse <Bject> Response = weBResponse.getSuccessResponse (null); réponse de retour; } @RequestMapping (value = "/ rest / article / {id}", méthode = get, produmes = "application / json") public webResponse <broile> getArticle (@pathvariable long id) {article article = ArticleService.getById (id); WeBResponse <Reate> Response = weBResponse.getSuccessResponse (article); réponse de retour; } @RequestMapping (value = "/ test / {id}", méthode = get, produmes = "application / json") public webResponse <?> Getnoapi () {webResponse <?> Réponse = webResponse.getSuccessResponse (null); réponse de retour; }}3. Configuration détaillée de l'API Swagger
Mais vous aurez certainement quelques questions lorsque vous verrez ceci:
La première question: il n'y a pas de description littérale du résultat de retour et des paramètres de demande. Cela peut-il être configuré?
La deuxième question: ce paramètre de demande doit être directement reflété en fonction de l'objet, mais toutes les propriétés de l'objet ne sont pas requises, et la valeur du paramètre peut ne pas répondre à nos besoins. Cela peut-il être configuré?
La réponse est certainement OK. Résolvons maintenant ces deux problèmes et examinons directement le code de configuration:
package com.pandy.blog.rest;import com.pandy.blog.dto.WebResponse;import com.pandy.blog.po.Article;import com.pandy.blog.service.ArticleService;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import io.swagger.annotations.apioperation; import io.swagger.annotations.apiresonse; import io.swagger.annotations.apirsonse; import io.swagger.annotations.apiresonse; import io.swagger.annotations.apireson; org.springframework.context.annotation.profile; import org.springframework.web.bind.annotation.pathvariable; import org.springframework.web.bind.annotation.requestbody; org.springframework.web.bind.annotation.restController; import java.util.hashmap; import java.util.list; import java.util.map; import static org.springframework.web.bind.annotation.requestmethod.delete; import static statique; org.springframework.web.bind.annotation.requestMethod.get; import static org.springframework.web.bind.annotation.wequestMethod.post; import static org.springframework.web.bind.annotation.requestMethod.put; @ reposTroller @ requestmapping (") Cassold") ArticlerestController {@Autowired Private ArticleService ArticleService; @Requestmapping (value = "/ article", méthode = post, produmes = "application / json") @apioperation (value = "ajouter l'article", notes = "ajouter un nouvel article", tags = "article", httpMethod = "Post") @APIIMPlicitParams (@APIIMPLICTARAM (name = "title", value = " @ApiimplicitParam (name = "résumé", value = "Résumé de l'article", obligatoire = true, dataType = "String"), @APIIMPLICtParam (name = "status", value = "Publish Status", Obligatoire = true, dataType = "Integer")}) @APiRESPONSES ({@APireSponse), 200, messages = "Success", Response = WHEBRESPONSE.CASE), ARTICHE), ARTICHE), CODE). WeBResponse <map <string, objet >> SAVEARTICLE (@Requestbody Article Article) {ArticleService.Savearticule (article); Map <string, objet> ret = new Hashmap <> (); ret.put ("id", article.getId ()); WeBResponse <map <string, objet >> réponse = weBResponse.getSuccessResponse (ret); réponse de retour; } @Apioperation (value = "supprimer l'article", notes = "supprimer l'article par id", tags = "article", httpMethod = "delete") @apiimplicitParams ({@apiimplicitParam (name = "id", valustmappre Delete, produit = "application / json") public webrePonse <?> Deletearticule (@Pathvariable long id) {Article Article = ArticleService.getById (id); Article.SetStatus (-1); ArticleService.Savearticule (article); return weBResponse.getSuccessResponse (new hashmap <> ()); } @Apioperation (value = "Obtenez la liste d'articles", notes = "requête complète selon le titre", tags = "article", httpMethod = "get") @apiimplicitParams ({@apiimplicitparam (name = "title", value = "Article Title", requise = false, datatype = "string"), @apiIMPLICTArA Articles par page ", obligatoire = false, dataType =" Integer "), @APIIMPLICtParam (name =" pagenum ", value =" PagePage Number ", requise = false, dataType =" Integer ")}) @RequestMapping (Value =" / Article / List ", Method = get, produces =" application / json ") public weBresponse <? Entier pagenum) {if (pagesize == null) {pageSize = 10; } if (pagenum == null) {pagenum = 1; } int offset = (pagenum - 1) * pagesize; Liste <ReTist> Articles = ArticleService.getarticles (titre, 1L, Offset, PageSize); retourner weBResponse.getSuccessResponse (articles); } @APIOperation (valeur = "Update Article", notes = "Update Article Content", Tags = "Article", httpMethod = "put") @apiimplicitParams ({@apiimplicitParam (name = "id", value = "Article Id", requise = true, datatype = "Long"), @APIimplicitParam (nom = "titre", "," = "String"), @APIIMPLICtParam (name = "Résumé", valeur = "Résumé de l'article", obligatoire = false, dataType = "String"), @APIIMPLICtParam (name = "Status", valeur = "Publish Status" WeBResponse <?> UpdateAralicle (@Pathvariable Long ID, @ requestbody Article) {Article.SetId (id); ArticleService.UpdateAralicle (article); return weBResponse.getSuccessResponse (new hashmap <> ()); }}Expliquons les fonctions spécifiques de plusieurs annotations et attributs connexes dans le code:
@ApiOperation, toute la configuration de l'attribut d'interface:
Valeur: Description de l'interface, affichée dans la liste d'interface.
Remarques: Description détaillée de l'interface, affichée sur la page Détails de l'interface.
Tags: la balise de l'interface. L'interface avec la même balise sera affichée sous une page d'onglet.
HttpMethod: méthode HTTP prise en charge.
@APIimplicitParams, un conteneur pour @apiimplicitParam, peut contenir plusieurs annotations @APIimplicitParam
@APIIMPLICtParam, Demandez le paramètre Configuration d'attribut:
Nom: Nom du paramètre
Valeur: Description du paramètre
requis: est-ce nécessaire
type de données: type de données
@APiResponses, @APiResponse Container, peut contenir plusieurs annotations @APiResponse
@APiResponse, renvoyez la configuration de l'attribut de résultat:
Code: renvoie le codage du résultat.
Message: renvoie la description du résultat.
Réponse: renvoie la classe correspondante du résultat.
Après avoir terminé la configuration ci-dessus, examinons l'effet de la page:
Page de liste:
Comme vous pouvez le voir, les interfaces sont maintenant situées sous la balise de l'article, et il y a également des instructions pour notre configuration derrière l'interface. Examinons la page Détails de l'interface "Post / REST / Article":
L'image est trop grande et seul l'affichage de l'attribut de titre est intercepté et les autres paramètres sont similaires. Nous pouvons voir sur la page qu'il existe des instructions pour les paramètres de demande, mais ce n'est pas l'effet à laquelle nous nous attendions. Si nos paramètres ne sont que des types simples, cette méthode devrait être bien, mais le problème est maintenant que nos paramètres de demande sont un objet, alors comment les configurer? Cela implique deux autres annotations: @apimodel et @apimodelproperty. Regardons d'abord le code, puis expliquons-le, ce qui est plus facile à comprendre:
@APIMODEL (Value = "Article Object", Description = "Add & Update Article Object Description") Public Class Article {@Id @geneRatedValue @apimodelproperty (name = "id", value = "Article ID", requis = false, example = "1") private long id; @Apimodelproperty (name = "title", value = "title de l'article", obligatoire = true, example = "Test Article Title") Title de chaîne privée; @Apimodelproperty (name = "résumé", value = "Résumé de l'article", requis = true, example = "Test Article Résumé") Résumé de la chaîne privée; @ApimodelProperty (Hidden = true) Private Date CreateTime; @APIMODELPROPERTY (HIDDEN = VRAI) DATE PRIVATE PUBLICTIVE; @ApimodelProperty (Hidden = true) Date privée UpdateTime; @ApimodelProperty (Hidden = true) privé Long UserId; @Apimodelproperty (name = "status", value = "Statut de libération de l'article", requis = true, example = "1") Statut entier privé; @Apimodelproperty (name = "type", value = "catégorie d'article", obligatoire = true, example = "1") type entier privé;}@Apimodel est la configuration des propriétés de toute la classe:
Valeur: Description de la classe
Description: Description détaillée
@Apimodelproperty est la configuration des propriétés de chaque champ en détail:
Nom: nom de champ
Valeur: description du champ
requis: est-ce nécessaire
Exemple: exemple de valeur
Hidden: s'il faut afficher
Après avoir terminé la configuration ci-dessus, examinons l'effet:
Maintenant, nous pouvons voir que la description du champ a été montrée, et la valeur du champ dans l'exemple est également devenue la valeur correspondante de l'exemple de la propriété que nous avons configurée. De cette façon, un document API complet est généré et le document est étroitement lié au code, plutôt que les deux pièces isolées. De plus, nous pouvons également le tester facilement via ce document. Nous devons simplement cliquer sur la boîte jaune sous une valeur d'exemple, et le contenu à l'intérieur sera automatiquement copié dans la zone de valeur correspondante de l'article, puis cliquez sur "Essayez-le" pour initier une demande HTTP.
Après avoir cliqué sur l'essayer, nous pouvons voir le résultat retourné:
L'opération est toujours très pratique. Par rapport à Junit et Postman, les tests via Swagger sont plus pratiques. Bien sûr, les tests de Swagger ne peuvent pas remplacer les tests unitaires, mais il a toujours un effet très important dans le débogage conjoint.
4. Résumé
Dans l'ensemble, la configuration de Swagger est relativement simple, et la capacité de Swagger à générer automatiquement des documents nous a effectivement fait économiser beaucoup de travail et a fourni une grande aide à la maintenance ultérieure. De plus, Swagger peut générer automatiquement des données de test pour nous en fonction de la configuration et fournir des méthodes HTTP correspondantes, ce qui est également utile pour nos travaux de débogage d'auto-test et de débogage conjoint. Par conséquent, je vous recommande toujours d'utiliser Swagger dans le développement quotidien, ce qui devrait vous aider à améliorer votre efficacité de travail dans une certaine mesure. Enfin, permettez-moi de laisser une question à réfléchir, c'est-à-dire que le document est accessible directement via la page, nous ne pouvons donc pas exposer l'interface directement à l'environnement de production, en particulier les systèmes qui doivent fournir des services externes. Alors, comment pouvons-nous désactiver cette fonction dans le processus de production? Il existe de nombreuses méthodes, vous pouvez l'essayer vous-même.
Ce qui précède est le combat réel du projet Spring Boot intégré Swagger2 présenté par l'éditeur. J'espère que cela vous sera utile. Si vous avez des questions, veuillez me laisser un message et l'éditeur vous répondra à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!