Swagger使用

一、Swagger介绍

1.1 swagger

Swagger3 是一套基于 OpenAPI 规范(OpenAPI Specification,OAS)构建的开源工具,后来成为了 Open API 标准的主要定义者。对于 Rest API 来说很重要的一部分内容就是文档,Swagger 为我们提供了一套通过代码和注解自动生成文档的方法,这一点对于保证API 文档的及时性将有很大的帮助。

swagger2于17年停止维护,现在最新的版本为17年发布的 Swagger3(Open Api3)。

swagger2需要两个依赖自动生成文档springfox-swagger2springfox-swagger-ui

  • springfox-swagger2:自动生成描述API的json文件

  • springfox-swagger-ui:将描述API的json文件解析出来,然后以ui界面的方式展示。

1.2 springfox

SpringFox是 spring 社区维护的一个项目(非官方)由于Spring的流行,Marty Pitt编写了一个基于Spring的组件swagger-springmvc,用于将swagger集成到springmvc中来,而springfox则是从这个组件发展而来。

二、Swagger2入门使用

2.1 创建项目

注意springboot版本,我这里选的2.5.6,版本太高启动会报错,后面会提到具体问题。

2.2 引入swagger2.9.2

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<!--swagger ui-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

2.3 创建SwaggerConfig

通过@EnableSwagger2开启swagger,然后配置需要生成文档的包名,以及作者,描述信息等内容。

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket buildDocket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(buildApiInf())
                .select()
                //指定包下的类生成接口文档
                .apis(RequestHandlerSelectors.basePackage("com.depart"))
                //包含ApiOperation注解的类,才生成接口文档
//                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(setHeaderToken());//请求头中添加token,便于接口测试
    }

    private ApiInfo buildApiInf(){
        return new ApiInfoBuilder()
                .title("xxx系统")
                .description("这是一个用于测试swagger2的项目")
                .contact(new Contact("wangzhen", "", "214556924@qq.com"))
                .version("1.0.0")
                .build();
    }

    private List<Parameter> setHeaderToken() {
        ParameterBuilder tokenPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<>();
        tokenPar.name("Access-Token").description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        pars.add(tokenPar.build());
        return pars;
    }
}

2.4 创建对应测试类

User类

@Data
@ApiModel(value = "User", description = "账户")
public class User {

    @ApiModelProperty(value = "用户ID")
    private String userId;
    @ApiModelProperty(value = "用户名")
    private String userName;

}

Controller类

@Api(value = "user", tags="用户管理")
@RestController
@RequestMapping("/user")
public class UserController {

        @ApiOperation(value = "根据用户ID获取用户信息", response = User.class, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @GetMapping(value = "getUserById")
    public User getUserById(
            @ApiParam(required = true, name = "userId", value = "用户ID")
            @RequestParam(value = "userId", required = true) String userId,
            @ApiParam(required = false, name = "userName", value = "用户名")
            @RequestParam(value = "userName", required = false) String userName
    ){
        return new User();
    }

    @ApiOperation(value = "根据用户ID获取用户信")
    @PostMapping(value = "addUser")
    public void addUser(User user){
    }

    @ApiOperation(value = "更新用户信息")
    @PostMapping(value = "updateUser")
    public void updateUser(User user){
    }

    @ApiOperation(value = "删除用户")
    @PostMapping(value = "deleteUser")
    @ApiImplicitParams({
            @ApiImplicitParam(name="userId",value="用户ID",required=true)
    })
    public void deleteUser(String userId) {
    }
}

2.5 查看接口文档

通过访问localhost:8080/swagger-ui.html查看,可以看到分为两部分,一部分是接口,另一部分是Model层。

Models部分很简单,重点看一下接口部分,展开如下图,可以看到@ApiOperation的value对应这下边的描述。

接口测试,点击右上角Try it out后,如下图,第一个参数Access-Token为请求头中的参数,这样测试会有点麻烦,如果不需要可以在SwaggerConfig类中删除,当然也有更优雅的方式,后面会提到。由于我们给User类配置了@ApiModel,所以参数User类就变成了一个一个参数形式。

对于单个参数,可以通过@ApiParam来标记

单个或多个参数也可以使用@ApiImplicitParams@ApiImplicitParam来统一标记。

2.6 小结与补充

以上部分是swagger常见的使用方式,以及常见swagger注解。

  • 其中注解本身还有很多其他参数,其他参数说明可以参考这篇博客swagger2 注解说明_那年那些事儿-CSDN博客_swagger注解

  • 关于springboot版本问题,选用springboot2.6.4配上swagger2.9.2会报错,错误如下,所以可以选择springboot2.5.6

    org.springframework.context.ApplicationContextException: 
    Failed to start bean 'documentationPluginsBootstrapper'; 
    nested exception is java.lang.NullPointerException
    
  • swagger2.8.0开始界面发生了较大的改变,但注解等关键内容变化不大。下面是swagger2.7.0的样式。

三、Swagger3使用

3.1 创建项目

注意springboot版本,我这里选的2.5.6,版本太高启动会报错,后面会提到具体问题。

编写application.yml文件

spring:
  application:
    name: xxx系统
server:
  port: 8081
# ===== 自定义swagger配置 ===== #
swagger:
  enable: true
  application-name: ${spring.application.name}
  application-version: 1.0.0
  application-description: 这是一个用于测试swagger2的项目
  try-host: http://localhost:${server.port}

3.2 引入swagger3.0.0

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

3.3 创建Swagger配置类

通过@EnableOpenApi@EnableSwagger2都可以开启swagger,然后配置需要生成文档的包名,以及作者,描述信息等内容。

@Configuration
//@EnableOpenApi//也可以不加,通过配置文件的enable参数控制
public class SwaggerConfig {
    private final SwaggerProperties swaggerProperties;

    public SwaggerConfig(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30).pathMapping("/")

                // 定义是否开启swagger,false为关闭,可以通过变量控制
                .enable(swaggerProperties.getEnable())

                // 将api的元信息设置为包含在json ResourceListing响应中。
                .apiInfo(apiInfo())

                // 接口调试地址
                .host(swaggerProperties.getTryHost())

                // 选择哪些接口作为swagger的doc发布
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()

                // 支持的通讯协议集合
                .protocols(newHashSet("https", "http"))

                // 授权信息设置,必要的header token等认证信息
                .securitySchemes(securitySchemes())

                // 授权信息全局应用
                .securityContexts(securityContexts());
    }

    /**
     * API 页面上半部分展示信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title(swaggerProperties.getApplicationName() + " Api Doc")
                .description(swaggerProperties.getApplicationDescription())
                .contact(new Contact("wangzhen", null, "214556924@qq.com"))
                .version("Application Version: " + swaggerProperties.getApplicationVersion() + ", Spring Boot Version: " + SpringBootVersion.getVersion())
                .build();
    }

    /**
     * 设置授权信息
     */
    private List<SecurityScheme> securitySchemes() {
        ApiKey apiKey = new ApiKey("ACCESS_TOKEN", "token", In.HEADER.toValue());
        return Collections.singletonList(apiKey);
    }

    /**
     * 授权信息全局应用
     */
    private List<SecurityContext> securityContexts() {
        return Collections.singletonList(
                SecurityContext.builder()
                        .securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN", new AuthorizationScope[]{new AuthorizationScope("global", "")})))
                        .build()
        );
    }

    @SafeVarargs
    private final <T> Set<T> newHashSet(T... ts) {
        if (ts.length > 0) {
            return new LinkedHashSet<>(Arrays.asList(ts));
        }
        return null;
    }

}
@Component
@ConfigurationProperties("swagger")
public class SwaggerProperties {
    /**
     * 是否开启swagger,生产环境一般关闭,所以这里定义一个变量
     */
    private Boolean enable;

    /**
     * 项目应用名
     */
    private String applicationName;

    /**
     * 项目版本信息
     */
    private String applicationVersion;

    /**
     * 项目描述信息
     */
    private String applicationDescription;

    /**
     * 接口调试地址
     */
    private String tryHost;

    //……省略getter和setter
}

3.4 查看接口文档

Controller还是原来的代码,通过访问localhost:8081/swagger-ui/index.html查看,可以看到分为两部分,一部分是接口,另一部分是Schemas层。

效果差不多,不过3.0可以添加有全局token。

3.5 小结与补充

swagger3与swagger2的主要区别

  1. 配置类上添加的注解是@EnableOpenApi,swagger2是@EnableSwagger2

  2. 访问地址http://localhost:8080/swagger-ui/index.html,swagger2是http://localhost:8080/swagger-ui.html

  3. pom引入只需springfox-boot-starter,swagger2有两个springfox-swagger2springfox-swagger-ui

四、版本问题和最佳实践

4.1 版本问题

在整合swagger时,经常会遇到版本问题,不过很好解决。这里记录一下之前遇到的一些版本问题。比如之前从swagger1升级到swagger2和学习过程中遇到的一些问题。

  • spring 4.3.29 + swagger2.9.2

导包时swagger-models会有问题,需要使用swagger-models1.5.22

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.9</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
    <!-- 排除自带的1.5.20版本-->
    <exclusions>
        <exclusion>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
        </exclusion>
     </exclusions>
</dependency>
<!-- 使用1.5.22-->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.5.22</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
  • spring 4.1.16 + swagger2.6.1

使用tags会出现不能展开子接口详情问题

@Api(value = "user", tags = "用户管理")
@Controller
@RequestMapping("user")
public class UserController extends BaseController {}

description参数已被弃用,利用swagger2word导出之后的是接口名是英文

@Api(value = "user", description = "用户管理")
@Controller
@RequestMapping("apply")
public class UserController  extends BaseController {}
  • springboot2.6.x + swagger2.9.2问题

…………

4.2 最佳实践

就目前而言,使用SpringBoot 2.2.x - 2.5.x能很好整合swagger2和swagger3。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <java.version>1.8</java.version>
    <swagger2.version>2.9.2</swagger2.version>
    <swagger-models.version>1.6.0</swagger-models.version>
    <swagger-annotations.version>1.6.0</swagger-annotations.version>
</properties>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${swagger2.version}</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${swagger2.version}</version>
</dependency>
<!--解决Swagger 2.9.2版本NumberFormatException-->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>${swagger-models.version}</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>${swagger-annotations.version}</version>
</dependency>

五、swagger文档转word

工作中经常会需要word形式的接口文档,这里推荐一个github地址和在线转换地址。

只需要获取文档接口json数据的地址就行。

swagger3:http://localhost:8081/v3/api-docs

swagger2:http://localhost:8081/v2/api-docs

Tips

注意这里只支持swagger2以上的版本,

GitHub - JMCuixy/swagger2word: 一个Swagger API 文档 转 Word 文档的工具项目

在线swagger转word文档|swagger导出word文档 - Kalvin在线工具