重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要介绍了Spring Boot如何集成Swagger2项目,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
创新互联专注于镶黄企业网站建设,响应式网站设计,商城系统网站开发。镶黄网站建设公司,为镶黄等地区提供建站服务。全流程按需定制,专业设计,全程项目跟踪,创新互联专业和态度为您提供的服务
一、Swagger简介
在日常的工作中,我们往往需要给前端(WEB端、IOS、Android)或者第三方提供接口,这个时候我们就需要给他们提供一份详细的API说明文档。但维护一份详细的文档可不是一件简单的事情。首先,编写一份详细的文档本身就是一件很费时费力的事情,另一方面,由于代码和文档是分离的,所以很容易导致文档和代码的不一致。这篇文章我们就来分享一种API文档维护的方式,即通过Swagger来自动生成Restuful API文档。
那什么是Swagger?我们可以直接看下官方的描述:
THE WORLD'S MOST POPULAR API TOOLING Swagger is the world's largest framework of API developer tools for the OpenAPI Specification(OAS), enabling development across the entire API lifecycle, from design and documentation, to test and deployment.
这段话首先告诉大家Swagger是世界上最流行的API工具,并且Swagger的目的是支撑整个API生命周期的开发,包括设计、文档以及测试和部署。这篇文章中我们会用到Swagger的文档管理和测试功能。
对Swagger的作用有了基本的认识后,我们现在来看看怎么使用。
二、Swagger与Spring boot集成
第一步:引入对应jar包:
io.springfox springfox-swagger2 2.6.0 io.springfox springfox-swagger-ui 2.6.0
第二步,基本信息配置:
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.pandy.blog.rest")) .paths(PathSelectors.regex("/rest/.*")) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Blog系统Restful API") .description("Blog系统Restful API") .termsOfServiceUrl("http://127.0.0.1:8080/") .contact("liuxiaopeng") .version("1.0") .build(); } }
基础的配置是对整个API文档的描述以及一些全局性的配置,对所有接口起作用。这里涉及到两个注解:
@Configuration是表示这是一个配置类,是JDK自带的注解,前面的文章中也已做过说明。
@EnableSwagger2的作用是启用Swagger2相关功能。
在这个配置类里面我么实例化了一个Docket对象,这个对象主要包括三个方面的信息:
(1)整个API的描述信息,即ApiInfo对象包括的信息,这部分信息会在页面上展示。
(2)指定生成API文档的包名。
(3)指定生成API的路径。按路径生成API可支持四种模式,这个可以参考其源码:
public class PathSelectors { private PathSelectors() { throw new UnsupportedOperationException(); } public static Predicateany() { return Predicates.alwaysTrue(); } public static Predicate none() { return Predicates.alwaysFalse(); } public static Predicate regex(final String pathRegex) { return new Predicate () { public boolean apply(String input) { return input.matches(pathRegex); } }; } public static Predicate ant(final String antPattern) { return new Predicate () { public boolean apply(String input) { AntPathMatcher matcher = new AntPathMatcher(); return matcher.match(antPattern, input); } }; } }
从源码可以看出,Swagger总共支持任何路径都生成、任何路径都不生成以及正则匹配和ant 模式匹配四种方式。大家可能比较熟悉的是前三种,最后一种ant匹配,如果不熟悉ant的话就直接忽略吧,前三种应该足够大家在日常工作中使用了。
有了上面的配置我们就可以看到效果了,我在com.pandy.blog.rest这个包下面有一个ArticleRestController这个类,源码如下:
启动Spring boot,然后访问:http://127.0.0.1:8080/swagger-ui.html即可看到如下结果:
这个页面上可以看到,除了最后一个接口/test/{id}外,其他接口都生成对应的文档,最后一个接口因为不满足我们配置的路径——“/rest/.*”,所以没有生成文档。
我们还可以点进去看一下每一个具体的接口,我们这里以“POST /rest/article”这个接口为例:
可以看到,Swagger为每一个接口都生成了返回结果和请求参数的示例,并且能直接通过下面的"try it out"进行接口访问,方面大家对接口进行测试。整体上感觉Swagger还是很强大的,配置也比较简单。
@RestController public class ArticleRestController { @Autowired private ArticleService articleService; @RequestMapping(value = "/rest/article", method = POST, produces = "application/json") public WebResponse
三、Swagger API详细配置
不过大家看到这里肯定会有点疑问:
第一个问题:这个返回结果和请求参数都没有文字性的描述,这个可不可以配置?
第二个问题:这个请求参应该是直接根据对象反射出来的结果,但是不是对象的每个属性都是必传的,另外参数的值也不一定满足我们的需求,这个能否配置?
答案肯定是可以的,现在我们就来解决这两个问题,直接看配置的代码:
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.ApiResponse; import io.swagger.annotations.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import 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; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.springframework.web.bind.annotation.RequestMethod.DELETE; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; import static org.springframework.web.bind.annotation.RequestMethod.PUT; @RestController @RequestMapping("/rest") public class ArticleRestController { @Autowired private ArticleService articleService; @RequestMapping(value = "/article", method = POST, produces = "application/json") @ApiOperation(value = "添加文章", notes = "添加新的文章", tags = "Article",httpMethod = "POST") @ApiImplicitParams({ @ApiImplicitParam(name = "title", value = "文章标题", required = true, dataType = "String"), @ApiImplicitParam(name = "summary", value = "文章摘要", required = true, dataType = "String"), @ApiImplicitParam(name = "status", value = "发布状态", required = true, dataType = "Integer") }) @ApiResponses({ @ApiResponse(code=200,message="成功",response=WebResponse.class), }) public WebResponse> saveArticle(@RequestBody Article article){ articleService.saveArticle(article); Map ret = new HashMap<>(); ret.put("id",article.getId()); WebResponse > response = WebResponse.getSuccessResponse(ret); return response; } @ApiOperation(value = "删除文章", notes = "根据ID删除文章", tags = "Article",httpMethod = "DELETE") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "Long") }) @RequestMapping(value = "/{id}",method = DELETE,produces = "application/json") public WebResponse> deleteArticle(@PathVariable Long id){ Article article = articleService.getById(id); article.setStatus(-1); articleService.saveArticle(article); return WebResponse.getSuccessResponse(new HashMap<>()); } @ApiOperation(value = "获取文章列表", notes = "可以根据标题进行模糊查询", tags = "Article",httpMethod = "GET") @ApiImplicitParams({ @ApiImplicitParam(name = "title", value = "文章标题", required = false, dataType = "String"), @ApiImplicitParam(name = "pageSize", value = "每页文章数量", required = false, dataType = "Integer"), @ApiImplicitParam(name = "pageNum", value = "分页的页码", required = false, dataType = "Integer") }) @RequestMapping(value = "/article/list", method = GET, produces = "application/json") public WebResponse> listArticles(String title, Integer pageSize, Integer pageNum) { if (pageSize == null) { pageSize = 10; } if (pageNum == null) { pageNum = 1; } int offset = (pageNum - 1) * pageSize; List articles = articleService.getArticles(title, 1L, offset, pageSize); return WebResponse.getSuccessResponse(articles); } @ApiOperation(value = "更新文章", notes = "更新文章内容", tags = "Article",httpMethod = "PUT") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "Long"), @ApiImplicitParam(name = "title", value = "文章标题", required = false, dataType = "String"), @ApiImplicitParam(name = "summary", value = "文章摘要", required = false, dataType = "String"), @ApiImplicitParam(name = "status", value = "发布状态", required = false, dataType = "Integer") }) @RequestMapping(value = "/article/{id}", method = PUT, produces = "application/json") public WebResponse> updateArticle(@PathVariable Long id,@RequestBody Article article){ article.setId(id); articleService.updateArticle(article); return WebResponse.getSuccessResponse(new HashMap<>()); } }
我们解释一下代码中几个注解及相关属性的具体作用:
@ApiOperation,整个接口属性配置:
value:接口说明,展示在接口列表。
notes:接口详细说明,展示在接口的详情页。
tags:接口的标签,相同标签的接口会在一个标签页下展示。
httpMethod:支持的HTTP的方法。
@ApiImplicitParams,@ApiImplicitParam的容器,可包含多个@ApiImplicitParam注解
@ApiImplicitParam,请求参数属性配置:
name:参数名称
value:参数说明
required:是否必须
dataType:数据类型
@ApiResponses,@ApiResponse容器,可以包含多个@ApiResponse注解
@ApiResponse,返回结果属性配置:
code:返回结果的编码。
message:返回结果的说明。
response:返回结果对应的类。
完成以上配置后,我们再看下页面效果:
列表页:
可以看到,现在接口都位于Article这个tag下,并且接口后面也有了我们配置好的说明。我们再看下”POST /rest/article“这个接口的详情页:
图片太大,只截取了title属性的展示,其他几个参数的类似。我们可以从页面上看到请求参数的说明是有的,不过这不是我们预期的效果,如果我们的参数仅仅是简单类型,这种方式应该没问题,但现在的问题是我们的请求参数是一个对象,那如何配置呢?这就涉及到另外两个注解:@ApiModel和@ApiModelProperty,我们还是先看代码,然后再解释,这样更容易理解:
@ApiModel(value="article对象",description="新增&更新文章对象说明") public class Article { @Id @GeneratedValue @ApiModelProperty(name = "id",value = "文章ID",required = false,example = "1") private Long id; @ApiModelProperty(name = "title",value = "文章标题",required = true,example = "测试文章标题") private String title; @ApiModelProperty(name = "summary",value = "文章摘要",required = true,example = "测试文章摘要") private String summary; @ApiModelProperty(hidden = true) private Date createTime; @ApiModelProperty(hidden = true) private Date publicTime; @ApiModelProperty(hidden = true) private Date updateTime; @ApiModelProperty(hidden = true) private Long userId; @ApiModelProperty(name = "status",value = "文章发布状态",required = true,example = "1") private Integer status; @ApiModelProperty(name = "type",value = "文章分类",required = true,example = "1") private Integer type; }
@ApiModel是对整个类的属性的配置:
value:类的说明
description:详细描述
@ApiModelProperty是对具体每个字段的属性配置:
name:字段名称
value:字段的说明
required:是否必须
example:示例值
hidden:是否显示
完成上面的配置后,我们再来看效果:
现在我们可以看到,字段的说明都已经展示出来,并且,示例中字段的值也变成了我们配置的example属性对应的值了。这样,一份完整的API文档就生成了,并且该文档与代码紧密的联系在一起,而不是隔离的两个部分。除此之外,我们还可以直接通过该文档很方便的进行测试,我们只需要点击Example Value下黄色的框,里面的内容就会自动复制到article对应的value框中,然后在点击“Try it out”就可以发起http请求了。
点击Try it out后,我们就可以看到返回的结果:
操作还是很方便的,相比Junit和postman,通过Swagger来测试会更加便捷,当然,Swagger的测试并不能代替单元测试,不过,在联调的时候还是有非常大的作用的。
感谢你能够认真阅读完这篇文章,希望小编分享的“Spring Boot如何集成Swagger2项目”这篇文章对大家有帮助,同时也希望大家多多支持创新互联,关注创新互联行业资讯频道,更多相关知识等着你来学习!