@
1、前言
在团队开发中,一个好的 API 文档不但可以减少大量的沟通成本,还可以帮助一位新人快速上手业务。传统的做法是由开发人员创建一份 RESTful API 文档来记录所有的接口细节,并在程序员之间代代相传。
这种做法存在以下几个问题:
-
API 接口众多,细节复杂,需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等,想要高质量的完成这份文档需要耗费大量的精力;
-
难以维护。随着需求的变更和项目的优化、推进,接口的细节在不断地演变,接口描述文档也需要同步修订,可是文档和代码处于两个不同的媒介,除非有严格的管理机制,否则很容易出现文档、接口不一致的情况
Swagger2 的出现就是为了从根本上解决上述问题。它作为一个规范和完整的框架,可以用于生成、描述、调用和可视化 RESTful 风格的 Web 服务:
-
接口文档在线自动生成,文档随接口变动实时更新,节省维护成本
-
支持在线接口测试,不依赖第三方工具,可测试文件和文本
-
可以导出离线 html 文档,方便合作和查看
2、SpringBoot 集成 Swagger2
2.1、pom.xml 添加 Maven 依赖
<!-- Swagger2 生成 API 所需依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2.2、创建 Swagger2Config.java
package per.cjh.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author cjh
* @description: Swagger2 生成接口文档
*/
@Slf4j
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2).
// api 文档相关个性化信息,具体信息在 apiInfo函数内部指定
apiInfo(apiInfo()).
pathMapping("/").
select().
// 扫描 controller 层的所有包
apis(RequestHandlerSelectors.basePackage("per.cjh.example.controller")).
// 可以根据url路径设置哪些请求加入文档,忽略哪些请求
paths(PathSelectors.any()).
build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//页面标题
.title("项目 API 开发文档")
//创建人
.contact(new Contact("Mr. Chen", "https://blog.csdn.net/fujuacm", "chenjionghuan@qq.com"))
//描述
.description("简单优雅的 RESTFul 风格")
//版本号
.version("1.0")
.build();
}
}
Swagger2Configuration.java 配置类主要是往容器中配置 Bean 对象,并添加基本的文档信息,简单了解即可。
该类通过 @Configuration 注解,让 SpringBoot 加载该配置类。再通过 @EnableSwagger2 注解来启用 Swagger2。成员方法 createRestApi 函数创建 Docket 的 Bean 之后,apiInfo() 用来创建该 Api 的基本信息(这些基本信息会展现在文档页面中)。select() 函数返回一个 ApiSelectorBuilder 实例用来控制哪些接口暴露给 Swagger 来展现,示例中采用指定扫描的包路径来定义,Swagger 会扫描该包下所有 Controller 定义的 API,并产生文档内容(除了被 @ApiIgnore 指定的请求)。
2.3、Controller 层 API 编写
在 Controller 层中需要使用到的注解为:
类注解:@Api(tags = “xxx的相关 API”) - tags 属性是对该类的总体描述
方法(接口)注解:@ApiOperation(“页码获取项目列表”) - 该注解是对接口的描述
@ApiImplicitParam(name = “page”, value = “页码”) - 该注解是对接口参数的描述,该参数名字为 page,含义为 页码。
@ApiImplicitParams( {
@ApiImplicitParam(),
@ApiImplicitParam()
}) - 该注解内部包含参数数组,用于多接口的参数
@ApiParam(name = “file”,value = “Excel 文件”, required = true) - 该注解作用于参数上,用于指定该参数为上传文件,可以在生成的在线测试页面上显示成文件上传框
@ApiIgnore - 该注解作用于方法参数上,用于指定省略该参数。通常用于省略 Request、Response、HttpSession
Controller 层实例:
package per.cjh.example.controller;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import per.cjh.example.domain.ExNews;
import per.cjh.example.domain.ExSchedule;
import per.cjh.example.domain.ExVideo;
import per.cjh.example.service.FileService;
import per.cjh.example.utils.FileUtil;
import springfox.documentation.annotations.ApiIgnore;
import javax.servlet.http.HttpSession;
import java.util.List;
/**
* @author cjh
* @description: 用于进度表、视频、新闻的上传 和进度表的下载
* /video/get -展示视频、/video/post -上传视频、/video/delete -删除视频
* /schedule/get -展示进度表、/schedule/post -上传进度表、/schedule/delete -删除进度表
* /news/get -展示新闻、/news/post -上传新闻、/news/delete -删除新闻
* @date 2020/5/4 18:11
*/
@Api(tags = "多媒体管理的相关 API")
@RestController
@Slf4j
public class FileController {
@Autowired
private FileService fileService;
@Autowired
private FileUtil fileUtil;
private String name;
// 文件框 + 忽略 Session + 多参数描述
@ApiOperation("上传进度表")
@ApiImplicitParams({
@ApiImplicitParam(name = "summary", value = "250字的新闻梗概")
})
@PostMapping("/schedule")
public String schedulePost(@ApiParam(name = "file",value = "进度表", required = true) MultipartFile file, String summary, @ApiIgnore HttpSession session) {
try {
// 把文件保存到服务器上,并且返回服务器文件绝对路径
String dest = fileUtil.upload(file, "schedule");
name = (String) session.getAttribute("name");
fileService.insertOne(file, "schedule", name, summary, dest);
return "上传成功";
} catch (Throwable e) {
log.error(e.toString());
return "上传失败";
}
}
2.4、启动 SpringBoot 项目
访问 http://localhost:8080/swagger-ui.html
2.5、在 Security 中的配置
Spring Boot 项目中如果集成了 Spring Security,在不做额外配置的情况下,Swagger2 文档会被拦截。解决方法是在 Security 的配置类中重写 configure 方法添加白名单即可:
@Override
public void configure ( WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/swagger-ui.html")
.antMatchers("/v2/**")
.antMatchers("/swagger-resources/**");
}
3、生成离线文档
导入生成 Markdown 语言离线文档所需的依赖和配置:
<!-- Swagger2导出接口 MarkDown 文件 -->
<dependency>
<groupId>io.github.swagger2markup</groupId>
<artifactId>swagger2markup</artifactId>
<version>1.3.1</version>
<scope>test</scope>
</dependency>
<!-- 没有下面这个下载不了 swagger2markup,还需要在 pom 文件中添加如下内容 -->
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>jcenter-releases</id>
<name>jcenter</name>
<url>http://jcenter.bintray.com</url>
</repository>
</repositories>
编写 SpringBoot 测试类,运行自己想要的文档生成方法。
package per.cjh.example;
import io.github.swagger2markup.GroupBy;
import io.github.swagger2markup.Language;
import io.github.swagger2markup.Swagger2MarkupConfig;
import io.github.swagger2markup.Swagger2MarkupConverter;
import io.github.swagger2markup.builder.Swagger2MarkupConfigBuilder;
import io.github.swagger2markup.markup.builder.MarkupLanguage;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.net.URL;
import java.nio.file.Paths;
@RunWith(SpringRunner.class)
@SpringBootTest
class GeneratorSwagger2API {
/**
* 生成AsciiDocs格式文档
* @throws Exception
*/
@Test
public void generateAsciiDocs() throws Exception {
// 输出Ascii格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
//从 http://localhost:8080/v2/api-docs 该 URL 中生成 API 文档,所以工程必须先运行起来。
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
.withConfig(config)
.build()
// 文档生成后所在的文件夹
.toFolder(Paths.get("./docs/asciidoc/generated"));
}
/**
* 生成Markdown格式文档
* @throws Exception
*/
@Test
public void generateMarkdownDocs() throws Exception {
// 输出Markdown格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
.withConfig(config)
.build()
// 生成指定目录、和文件
.toFolder(Paths.get("./docs/markdown/generated"));
}
/**
* 生成Confluence格式文档
* @throws Exception
*/
@Test
public void generateConfluenceDocs() throws Exception {
// 输出Confluence使用的格式
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.CONFLUENCE_MARKUP)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
.withConfig(config)
.build()
.toFolder(Paths.get("./docs/confluence/generated"));
}
/**
* 生成AsciiDocs格式文档,并汇总成一个文件
* @throws Exception
*/
@Test
public void generateAsciiDocsToFile() throws Exception {
// 输出Ascii到单文件
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.ASCIIDOC)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
.withConfig(config)
.build()
.toFile(Paths.get("./docs/asciidoc/generated/all"));
}
/**
* 生成Markdown格式文档,并汇总成一个文件
* @throws Exception
*/
@Test
public void generateMarkdownDocsToFile() throws Exception {
// 输出Markdown到单文件
Swagger2MarkupConfig config = new Swagger2MarkupConfigBuilder()
.withMarkupLanguage(MarkupLanguage.MARKDOWN)
.withOutputLanguage(Language.ZH)
.withPathsGroupedBy(GroupBy.TAGS)
.withGeneratedExamples()
.withoutInlineSchema()
.build();
Swagger2MarkupConverter.from(new URL("http://localhost:8080/v2/api-docs"))
.withConfig(config)
.build()
.toFile(Paths.get("./docs/markdown/generated/all"));
}
}
4、生成 html 页面文档
目的:将上一步生成的 AsciiDocs 格式文档转化为 html 页面文档。
- 导入 maven 插件生成 HTML 文档
<!-- 使用 maven 插件生成 HTML 文档 -->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<configuration>
<sourceDirectory>./docs/asciidoc/generated</sourceDirectory>
<outputDirectory>./docs/asciidoc/html</outputDirectory>
<headerFooter>true</headerFooter>
<doctype>book</doctype>
<backend>html</backend>
<sourceHighlighter>coderay</sourceHighlighter>
<attributes>
<!--菜单栏在左边-->
<toc>left</toc>
<!--多标题排列,显示层级数-->
<toclevels>3</toclevels>
<!--自动打数字序号-->
<sectnums>true</sectnums>
</attributes>
</configuration>
</plugin>
- 在插件列表中,点击箭头所在位置,即可生成 html 文档