重学Spring系列之Swagger2.0和Swagger3.0

2021-12-07 18:42:19 浏览数 (1)

  • 整合springdoc-openapi
  • 将API分组分组展示
  • 使用 swagger3 注解代替 swagger2注解

使用Swagger2构建API文档

为什么要发布API接口文档

当下很多公司都采取前后端分离的开发模式,前端和后端的工作由不同的工程师完成。在这种开发模式下,维护一份及时更新且完整的API 文档将会极大的提高我们的工作效率。传统意义上的文档都是后端开发人员使用word编写的,相信大家也都知道这种方式很难保证文档的及时性,这种文档久而久之也就会失去其参考意义,反而还会加大我们的沟通成本。而 Swagger 给我们提供了一个全新的维护 API 文档的方式,下面我们就来了解一下它的优点

  • 代码变,文档变。只需要少量的注解,Swagger 就可以根据代码自动生成 API 文档,很好的保证了文档的时效性。
  • 跨语言性,支持 40 多种语言。
  • Swagger UI 呈现出来的是一份可交互式的 API 文档,我们可以直接在文档页面尝试 API的调用,省去了准备复杂的调用参数的过程。
  • 还可以将文档规范导入相关的工具(例如 SoapUI), 这些工具将会为我们自动地创建自动化测试。

整合swagger2生成文档

首先通过maven坐标引入swagger相关的类库。

代码语言:javascript复制
<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger2</artifactId>
	<version>2.6.1</version>
</dependency>

<dependency>
	<groupId>io.springfox</groupId>
	<artifactId>springfox-swagger-ui</artifactId>
	<version>2.6.1</version>
</dependency>

然后通过java Config对Swagger2进行配置。

代码语言:javascript复制
@Configuration
@EnableSwagger2
public class Swagger2 {

    private ApiInfo apiInfo() {
		return new ApiInfoBuilder()
				.title("springboot利用swagger构建api文档")
				.description("简单优雅的restfun风格")
				.termsOfServiceUrl("https://blog.csdn.net/m0_53157173")
				.version("1.0")
				.build();
    }

	@Bean
	public Docket createRestApi() {
		return new Docket(DocumentationType.SWAGGER_2)
                        .apiInfo(apiInfo())
                        .select()
                        //扫描basePackage包下面的“/rest/”路径下的内容作为接口文档构建的目标
                        .apis(RequestHandlerSelectors.basePackage("com.controller"))
                        .paths(PathSelectors.regex("/rest/.*"))
                        .build();
	}
	
	
}
  • @EnableSwagger2 注解表示开启SwaggerAPI文档相关的功能
  • 在apiInfo方法中配置接口文档的title(标题)、描述、termsOfServiceUrl(服务协议)、版本等相关信息
  • 在createRestApi方法中,basePackage表示扫描哪个package下面的Controller类作为API接口文档内容范围
  • 在createRestApi方法中,paths表示哪一个请求路径下控制器映射方法,作为API接口文档内容范围

集成完成之后,做一下访问验证:http://localhost/swagger-ui.html ,如下:

swagger不仅提供了静态的接口文档的展示,还提供了执行接口方法测试的功能。在下图中填入接口对应的参数,点击“try it out"就可以实现接口请求的发送与响应结果的展示。


书写swagger注解

通常情况下Controller类及方法书写了swagger注解,就不需要写java注释了。但是笔者从来几乎都不写swagger注解,也就是说本小节(三、书写swagger注解)所讲的内容笔者从来都不用。因为一个成熟的团队,前端人员根据英文方法的名称和参数名称就能知道方法的作用,前提是代码开发者认真的为接口及参数起英文名。通过团队内推广RESTful接口的设计原则和良好的统一的交互规范,就能知道响应结果的含义。这也是一种“约定大于配置”的体现。

当然,如果你的团队没有“约定“,那么就需要“配置”来做文档说明。我通常把这个过程叫做“为接口功能添加注释”。写法如下:

代码语言:javascript复制
@ApiOperation(value = "添加文章", notes = "添加新的文章", tags = "Article",httpMethod = "POST")
@ApiImplicitParams({
        @ApiImplicitParam(name = "title", value = "文章标题", required = true, dataType = "String"),
        @ApiImplicitParam(name = "content", value = "文章内容", required = true, dataType = "String"),
        @ApiImplicitParam(name = "author", value = "文章作者", required = true, dataType = "String")
})
@ApiResponses({
        @ApiResponse(code=200,message="成功",response=AjaxResponse.class),
})
@PostMapping("/article")
public @ResponseBody  AjaxResponse saveArticle(
        @RequestParam(value="title") String title,  //参数1
        @RequestParam(value="content") String content,//参数2
        @RequestParam(value="author") String author,//参数3
) {

swagger注释添加完成之后,接口文档变成如下的样子(含有中文说明):

ApiModel注解的用法

代码语言:javascript复制
@ApiModel(value = "通用响应数据结构类")
public class AjaxResponse {
  @ApiModelProperty(value="请求是否处理成功")
  private boolean isok;  //请求是否处理成功
  @ApiModelProperty(value="请求响应状态码",example="200、400、500")
  private int code; //请求响应状态码(200、400、500)
  @ApiModelProperty(value="请求结果描述信息")
  private String message;  //请求结果描述信息
  @ApiModelProperty(value="请求结果数据")
  private Object data; //请求结果数据(通常用于查询操作)
}

页面显示结果


虽然笔者自己从来都不写swagger注解,但不排除有的团队觉得可以用,所以还是详细介绍一下:

代码语言:javascript复制
@Api:用在Controller控制器类上
     属性tags="说明该类的功能及作用"

@ApiOperation:用在Controller控制器类的请求的方法上
    value="说明方法的用途、作用"
    notes="方法的备注说明"

@ApiImplicitParams:用在请求的方法上,表示一组参数说明
    @ApiImplicitParam:请求方法中参数的说明
        name:参数名
        value:参数的汉字说明、解释、用途
        required:参数是否必须传,布尔类型
        paramType:参数的类型,即参数存储位置或提交方式
            · header --> Http的Header携带的参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam   (如上面的例子)
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · body(不常用)
            · form(不常用)    
        dataType:参数类型,默认String,其它值dataType="Integer"       
        defaultValue:参数的默认值

@ApiResponses:用在控制器的请求的方法上,对方法的响应结果进行描述
    @ApiResponse:用于表达一个响应信息
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:响应结果封装类,如上例子中的AjaxResponse.class

@ApiModel:value=“通常用在描述@RequestBody和@ResponseBody注解修饰的接收参数或响应参数实体类”
    @ApiModelProperty:value="实体类属性的描述"

生产环境下如何禁用swagger2

我们的文档通常是在团队内部观看及使用的,不希望发布到生产环境让用户看到。

  • 禁用方法1:使用注解@Profile({“dev”,“test”})表示在开发或测试环境开启,而在生产关闭。
  • 禁用方法2:使用注解@ConditionalOnProperty(name = “swagger.enable”,havingValue = “true”) 然后在测试配置或者开发配置中 添加 swagger.enable = true即可开启,生产环境不填则默认关闭Swagger.

使用Swagger2Markup实现导出API文档

Swagger2Markup是Github上的一个开源项目。该项目主要用来将Swagger自动生成的文档转换成几种流行的格式以便于静态部署和使用,比如:AsciiDoc、Markdown、Confluence。

生成AsciiDoc

生成AsciiDoc的方式有两种:

通过Java代码来生成

第一步:编辑pom.xml增加需要使用的相关依赖和仓库

代码语言:javascript复制
<dependency>
    <groupId>io.github.swagger2markup</groupId>
    <artifactId>swagger2markup</artifactId>
    <version>1.3.3</version>
</dependency>
<repositories>
    <repository>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>always</updatePolicy>
        </snapshots>
        <id>jcenter-releases</id>
        <name>jcenter</name>
        <url>http://jcenter.bintray.com</url>
    </repository>
</repositories>

第二步:编写一个单元测试用例来生成执行生成文档的代码

代码语言:javascript复制
/**
     * 生成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();

        Swagger2MarkupConverter.from(new URL("http://127.0.0.1:8082/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:8082/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:8082/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:8082/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:8082/v2/api-docs"))
                .withConfig(config)
                .build()
                .toFile(Paths.get("./docs/markdown/generated/all"));
    }

以上代码内容很简单,大致说明几个关键内容:

  • MarkupLanguage.ASCIIDOC:指定了要输出的最终格式。除了ASCIIDOC之外,还有MARKDOWN和CONFLUENCE_MARKUP
  • from(new URL("http://localhost:8080/v2/api-docs"):指定了生成静态部署文档的源头配置,可以是这样的URL形式,也可以是符合Swagger规范的String类型或者从文件中读取的流。如果是对当前使用的Swagger项目,我们通过使用访问本地Swagger接口的方式,如果是从外部获取的Swagger文档配置文件,就可以通过字符串或读文件的方式
  • toFolder(Paths.get("src/docs/asciidoc/generated"):指定最终生成文件的具体目录位置

如果不想分割结果文件,也可以通过替换toFolder(Paths.get("src/docs/asciidoc/generated")toFile(Paths.get("src/docs/asciidoc/generated/all")),将转换结果输出到一个单一的文件中,这样可以最终生成html的也是单一的。

在执行了上面的测试用例之后,我们就能在当前项目的目录下获得如下内容:

可以看到,这种方式在运行之后就生成出了5个不同的静态文件。


通过Maven插件来生成

除了通过上面编写Java代码来生成的方式之外,swagger2markup还提供了对应的Maven插件来使用。对于上面的生成方式,完全可以通过在pom.xml中增加如下插件来完成静态内容的生成。

代码语言:javascript复制
<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>

配置执行命令

通过上面的配置,执行该插件的asciidoctor:process-asciidoc命令之后,就能在docs/asciidoc/html目录下生成最终可用的静态部署HTML了。在完成生成之后,可以直接通过浏览器来看查看,你就能看到类似下图的静态部署结果:


可以参考的文章

使用Swagger2Markup实现导出API文档

Swagger文档转Word 文档

github

spring boot2.0 swagger自动生成PDF和HTML格式的API文档

swagger asciidoctor 导出PDF中文缺失乱码问题解决


Swagger3-即OpenAPI使

OpenAPI是规范的正式名称。规范的开发工作于2015年启动,当时SmartBear(负责Swagger工具开发的公司)将Swagger 2.0规范捐赠给了Open API Initiative,该协会由来自技术领域不同领域的30多个组织组成。此后,该规范被重命名为OpenAPI规范。

Swagger

  • 是一个 API文档维护组织,后来成为了 Open API 标准的主要定义者。现在最新的版本为17年发布的 Swagger3(Open Api3)。
  • 是一个Open API规范实现工具包,由于Swagger工具是由参与创建原始Swagger规范的团队开发的,因此通常仍将这些工具视为该规范的代名词。目前可以认为Swagger3就是Open API 3.0

OpenAPI 3.0:2017年7月,Open API Initiative最终发布了OpenAPI Specification 3.0.0。它对2.0规范进行了很多改进。Open API 3.0规范可以用JSON或YAML编写,并且在记录RESTful API方面做得很好。同时标志着Swagger2成为过去式。

SpringFox是 spring 社区维护的一个项目(非官方),帮助使用者将 swagger2 集成到 Spring 中。常常用于 Spring 中帮助开发者生成文档,并可以轻松的在spring boot中使用。截至2020年4月,尚未支持 OpenAPI3 标准。

SpringDoc也是 spring 社区维护的一个项目(非官方),帮助使用者将 swagger3 集成到 Spring 中。

也是用来在 Spring 中帮助开发者生成文档,并可以轻松的在spring boot中使用


整合springdoc-openapi

在pom.xml里面去掉springfox,添加如下的openapi依赖。

代码语言:javascript复制
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.4.0</version>
</dependency>

就这么简单,文档就构建完成了,不需要做任何的其他配置。

访问:http://localhost:8888/swagger-ui.html


将API分组分组展示

配置方法

代码语言:javascript复制
@Configuration
public class OpenAPIConfig {

  @Bean
  public GroupedOpenApi restApi() {
    return GroupedOpenApi.builder()
            .group("rest-api")
            .pathsToMatch("/rest/**")
            .build();
  }

  @Bean
  public GroupedOpenApi helloApi() {
    return GroupedOpenApi.builder()
            .group("hello")
            .pathsToMatch("/hello/**")
            .build();
  }


}

显示效果,通过下拉选择分组,查看组内API


使用 swagger3 注解代替 swagger2注解

如果你希望为文档加上更详细的中文注释,使用如下注解(对比Swagger2注解使用方法使用即可)。但是笔者觉得没有必要学习这东西,意义不大。注意变量、接口命名规范,英文的接口文档就挺好。

用 swagger 3 的注解(已经在上面maven包引入)代替 swagger 2 的注解,swagger 3 注解的包路径为io.swagger.v3.oas.annotations。


swagger3.0 第三方版本参考:

swagger3

0 人点赞