Spring boot 集成swagger-ui 在线API文档生成自我实践记录

目录

一、swagger简介

二、swagger原理

三、为什么选择swagger?

手写Api文档的几个痛点

swagger的优势

四、如何集成swagger到我们的项目?

五、总结常用swagger-ui注解

1)API的注解

- @Api() 用于类

- @ApiOperation() 用于方法

- @ApiParam() 用于方法,参数,字段说明

- @ApiResponses()用于包装类

- @ApiResponse()用于方法的返回结果

2)Model的注解

- @ApiModel() 用于类

- @ApiModelProperty() 用于model类的属性

3)其他注解

六、接口分组

路径分组

注解分组


一、swagger简介

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的API文档生成工具。

Swagger™的目标是为REST APIs 定义一个标准的,与语言无关的接口,使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。

二、swagger原理

首先看下springfox对文档Documentation的定义:

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

文档Documentation定义得很清晰,主要由groupName(分组名)、basePath(contextPath)、apiListings(API列表集)、resourceListing(资源列表集)等属性组成。

其中API列表被封装成ApiListing。ApiListing中又持有ApiDesciption集合引用,每个ApiDesciption都持有一个API集合的引用,Operation也就是具体的接口操作,内部包含了该接口对应的http方法、produces、consumes、协议、参数集、响应消息集等诸多元素。

springfox通过spring-plugin的方式将Plugin注册到Spring上下文中,然后使用这些plugin进行API的扫描工作,这里的扫描工作其实也就是构造Documentation的工作,把扫描出的结果封装成Documentation并放入到DocumentationCache内存缓存中,之后swagger-ui界面展示的API信息通过Swagger2Controller暴露,Swagger2Controller内部直接从DocumentationCache中寻找Documentation。

下面取部分plugin来看一下构造对应文档信息的过程:

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

从代码细节中入手,我们可以看到,入口处在@EnableSwagger2这个注解上,这个注解会导入一个配置类的Swagger2DocumentationConfiguration。

这个Swagger2DocumentationConfiguration做的事情如下:

1. 构造Bean。比如:HandlerMapping(HandlerMapping是springmvc中用于处理请求与handler(controller中的方法)之间映射关系的接口,springboot中默认使用的HandlerMapping是RequestMappingHandlerMapping),Swagger2DocumentationConfiguration配置类里构造的是PropertySourcedRequestMappingHandlerMapping,该类继承RequestMappingHandlerMapping。

2. import其它配置类,比如SpringfoxWebMvcConfiguration、SwaggerCommonConfiguration(SpringfoxWebMvcConfiguration配置类做的事情跟Swagger2DocumentationConfiguration类似)

3. 扫描指定包下的类,并注册到Spring上下文中

具体的API解析、扫描过程,这里不再展开细致的说明,可参考博文:https://yq.aliyun.com/articles/599809?utm_content=m_1000002417

总体上处理过程整理为下图:

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

三、为什么选择swagger?

手写Api文档的几个痛点

  1. 文档需要更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时;
  2. 接口返回结果不明确;
  3. 不能直接在线测试接口,通常需要使用工具,比如postman;
  4. 接口文档太多,不好管理;

swagger的优势

  1. 使用Swagger UI生成的界面比Javadoc生成的界面美观
  2. swagger可以实时同步API文档(代码修改后,文档同步修改)
  3. swagger解析速度快,效率高(使用轻量级数据交换格式JSON)
  4. 对现有SpringMVC工程支持友好
  5. Swagger可以充当前后端交流的重要桥梁,方便快捷。很实用。
  6. Swagger项目允许你生产,显示和消费你自己的RESTful服务。不需要代理和第三方服务。

四、如何集成swagger到我们的项目?

说明:这里是在Spring boot的基础上集成swagger

  1. 添加maven依赖
    <!-- 引入swagger依赖 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>${swagger.version}</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>${swagger.version}</version>
    </dependency>

     

  2. 定义配置类
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    /**
     * @Descrption swagger2配置类
     * @Date 2019-03-05 11:03
     * @Version 1.0
     */
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.xxx.xxx.api.controller"))
                    .paths(PathSelectors.any())
                    .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("SzcApi项目在线API")
                    .description("接口风格统一为Restful")
                    .build();
        }
    
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("swagger-ui.html")
                    .addResourceLocations("classpath:/METE-INF/resources/");
    
            registry.addResourceHandler("webjars/**")
                    .addResourceLocations("classpath:/METE-INF/resources/webjars/");
        }
    
    }

     

如上代码所示,

@Configuration注解,作用是让Spring来加载该类配置,@EnableSwagger2注解来启用Swagger2。

createRestApi函数创建Docket的Bean之后,通过apiInfo()来创建该Api的基本信息,这些基本信息会展现在文档页面中。select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现,我们这里采用指定扫描的包路径来定义,Swagger会扫描这个包下所有Controller定义的API,并产生文档内容(除了被@ApiIgnore指定的请求)。

到这里,我们就已经做到把swagger集成到项目中了,启动服务之后访问url:http://localhost:7700/swagger-ui.html

,就可以看到swagger的API文档了。

注意:如果项目中有权限拦截,比如URL白名单,需要将swagger的路径加入到白名单列表中:

"/swagger", "/webjars", "/v2/api"

五、总结常用swagger-ui注解

1)API的注解

对于API的设计,一般倾向于将功能相同的API归集为一组。在Spring Boot中,利用Controller来实现,每个Controller里包含若干个REST API,而每个API都有输入及输出值。所以swagger对API的注解也是参照这个层级来划分与实现的。其逻辑结果如下图:

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

- @Api() 用于类

          该注解将一个Controller(Class)标注为一个swagger资源(API)。在默认情况下,Swagger-Core只会扫描解析具有@Api注解的类,而会自动忽略其他类别资源(JAX-RS endpoints,Servlets等等)的注解。该注解包含以下几个重要属性:

  • tags:API分组标签。具有相同标签的API将会被归并在一组内展示。
  • value:如果tags没有定义,value将作为Api的tags使用
  • description:对该API的详细描述信息
  • position:如果一个controller中有多个请求方法,可以通过该属性来指定API在swagger-ui中的显示顺序

- @ApiOperation() 用于方法

           在指定的(路由)路径上,对一个操作或HTTP方法进行描述。具有相同路径的不同操作会被归组为同一个操作对象。不同的HTTP请求方法及路径组合构成一个唯一操作。此注解的属性有:

  • value:对操作的简单说明,长度为120个字母,60个汉字。
  • notes:对操作的详细说明。
  • httpMethod:HTTP请求的动作名,可选值有:"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH"。
  • code:默认为200,有效值必须符合标准的HTTP Status Code Definitions。

- @ApiParam() 用于方法,参数,字段说明

增加对参数的元信息说明,主要的属性有:

  • required:指定该参数是否为必传参数
  • value:对该参数含义的简短说明

- @ApiResponses()用于包装类

注解@ApiResponse的包装类,数组结构。

即使需要使用一个@ApiResponse注解,也需要将@ApiResponse注解包含在注解@ApiResponses内。

- @ApiResponse()用于方法的返回结果

描述一个操作可能的返回结果。

当REST API请求发生时,这个注解可用于描述所有可能的成功与错误码。

可以用,也可以不用这个注解去描述操作的返回类型,但成功操作的返回类型必须在@ApiOperation中定义。

如果API具有不同的返回类型,那么需要分别定义返回值,并将返回类型进行关联。

但Swagger不支持同一返回码,多种返回类型的注解。注意:这个注解必须被包含在@ApiResponses注解中。

字段说明:

  • code:HTTP请求返回码。有效值必须符合标准的HTTP Status Code Definitions。
  • message:用于对返回信息作详细说明,对请求结果的描述信息
  • response:返回类型信息,必须使用完全限定类名,比如“com.xyz.cc.Person.class”。
  • responseContainer:如果返回类型为容器类型,可以设置相应的值。有效值为 "List", "Set" or "Map",其他任何无效的值都会被忽略

2)Model的注解

- @ApiModel() 用于类

提供对Swagger model额外信息的描述。在标注@ApiOperation注解的操作内,所有的类将自动被内省(introspected),但利用这个注解可以做一些更加详细的model结构说明。主要属性有:

  • value:model的别名,默认为类名
  • description:对model的详细描述

- @ApiModelProperty() 用于model类的属性

表示对model属性的说明或者数据操作更改,主要的属性有:

  • value:给出该属性的简短描述
  • required:标识该属性是否为必须值
  • example:给出该属性的示例值

3)其他注解

- @ApiIgnore() 用于类,方法,方法参数

表示这个方法或者类被忽略

- @ApiImplicitParam() 用于方法

表示单独的请求参数

- @ApiImplicitParams()  用于方法

该注解可以包含多个 @ApiImplicitParam

六、接口分组

接口分组就是将多个接口按照功能,或者按照模块分成多个组,这样查看AP文档的时候,可以相对清晰一些。

当然,分组只是推荐的处理方式,你也可以不分组。下面我们看一下分组有哪些实现方式:

路径分组

通过RequestHandlerSelectors.basePackage(“”)指定扫描的包路径。

假设我们在做一个学校的管理系统,后台用户分为校长、老师、学生。我们可以按照如下的方式对controller进行细分。

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

在我们的swagger配置类中增加三个Docket,配置如下:

@Bean
public Docket createPresidentDocket() {
	return new Docket(DocumentationType.SWAGGER_2).groupName("校长").apiInfo(presidentApiInfo()).select()
			.apis(RequestHandlerSelectors.basePackage("pri.fly.leaning.controller.president"))
			.paths(PathSelectors.any()).build();
}
@Bean
public Docket createStudentDocket() {
	return new Docket(DocumentationType.SWAGGER_2).groupName("学生").apiInfo(studentApiInfo()).select()
			.apis(RequestHandlerSelectors.basePackage("pri.fly.leaning.controller.student"))
			.paths(PathSelectors.any()).build();
}
@Bean
public Docket createTeacherDocket() {
	return new Docket(DocumentationType.SWAGGER_2).groupName("老师").apiInfo(teacherApiInfo()).select()
			.apis(RequestHandlerSelectors.basePackage("pri.fly.leaning.controller.teacher"))
			.paths(PathSelectors.any()).build();
}

 

然后我们就看到swagger UI的space处就可以选择分组了:

Spring boot 集成swagger-ui 在线API文档生成自我实践记录

注解分组

上文提到的路径分组是一种分组方式,但是这种分组的局限比较大,万一我们的代码中没有按照这种方式分包那就无法分组。现在来介绍一个更灵活的方式:注解分组

仍然以上面提到的学校管理系统为例,校长和老师都属于学校管理层,我们希望将他们放到同一个分组中。首先定义我们自己的分组注解。

/**
 * 学校管理层分组注解
 * @author fly
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Manager {
}

 

在对应的controller上加上我们自己定义的注解:

@RestController
@RequestMapping("/teacher/users")
@Api(description = "老师班级管理接口")
@Manager
public class ClassController {
	....
}


@RestController
@RequestMapping("/president/school")
@Api(description = "校长学校管理接口")
@Manager
public class SchoolController {
	....
}

 

配置Docket

核心:RequestHandlerSelectors.withClassAnnotation(Manager.class)

@Bean
public Docket createManagerDocket() {
	return new Docket(DocumentationType.SWAGGER_2).groupName("管理者").apiInfo(managerApiInfo()).select()
			.apis(RequestHandlerSelectors.withClassAnnotation(Manager.class))
			.paths(PathSelectors.any()).build();