# Swagger

# 1. 什么是 Swagger

Swagger 是一系列 RESTful API 的工具,通过 Swagger 可以获得项目的⼀种交互式文档,客户端 SDK 的自 动生成等功能。

Swagger 的目标是为 REST APIs 定义一个标准的、与语⾔言无关的接口,使人和计算机在看不到源码或者看不到文档或者不能通过网络流量检测的情况下,能发现和理解各种服务的功能。当服务通过 Swagger 定义,消费者就能与远程的服务互动通过少量的实现逻辑。

Swagger(丝袜哥)是世界上最流行的 API 表达工具。

# 2. 快速上手

使用 Spring Boot 集成 Swagger 的理念是,使用用注解来标记出需要在 API 文档中展示的信息,Swagger 会根据项目中标记的注解来生成对应的 API 文档。Swagger 被号称世界上最流行的 API 工具,它提供了 API 管理的全套解决方案,API 文档管理需要考虑的因素基本都包含,这里将讲解最常用的定制内容。

Spring Boot 集成 Swagger 3 很简单,需要引入依赖并做基础配置即可。

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

# 3. 创建 SwaggerConfig 配置类

@Configuration
@EnableSwagger2 
public class SwaggerConfig {
}

在 SwaggerConfig 的类上添加两个注解:

 @Configuration: 启动时加载此类 
@EnableSwagger2: 表示此项目启用 Swagger API 文档功能 

在 SwaggerConfig 中添加两个方法:(其中一个方法是为另一个方法作辅助的准备工作)

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
        .apiInfo(apiInfo())
        .select()
        // 此处自行修改为自己的 Controller 包路径。
        .apis(RequestHandlerSelectors.basePackage("xxx.yyy.zzz"))
        .paths(PathSelectors.any())
        .build();
}

此方法使用 @Bean,在启动时初始化,返回实例 Docket(Swagger API 摘要对象),这里需要注意的是 .apis(RequestHandlerSelectors.basePackage("xxx.yyy.zzz")) 指定需要扫描的包路路径,只有此路径下的 Controller 类才会自动生成 Swagger API 文档。

private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
       .title("XXX 项目接口文挡")
       .description("XXX Project Swagger2 UserService Interface")
       .termsOfServiceUrl("http://localhost:8080/swagger-ui/index.html")
       .version("1.0")
       .build();
}

这块配置相对重要一些,主要配置页面展示的基本信息包括,标题、描述、版本、服务条款等,查看 ApiInfo 类的源码还会发现支持 license 等更多的配置。

# 4. 访问 Swagger 404 问题

不出意外的话,你访问 /swagger-ui/index.html 会出现 404 。

这是因为,当你访问 .html 等静态资源时,Spring MVC 默认是在你自己项目的 resources/static 目录下去找它们,如果没有,自然就是 404 。

而 swagger 相关的 .html 等静态资源是在你所依赖、所包含的 swagger-ui 项目(jar包)

swagger-01

所以,你需要通过静态资源配置来『告诉』Spring MVC 当收到 swagger 相关的静态资源访问时,去指对应的这个目录下找它们。

@Configuration
@EnableOpenApi
public class SwaggerConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
    }

    /* 非必须
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/swagger-ui/")
                .setViewName("forward:/swagger-ui/index.html");
    }
    */

    ...

}

配置完成之后启动项目,在浏览器中输入网址 http://localhost:8080/swagger-ui/index.html (opens new window) ,即可看到上面的配置信息。

# 4. Swagger 常用注解

Swagger 通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息等,常用注解内容如下:

  • @Api 注解和 @ApiOperation 注解:@Api 注解标注在 Controller 类上,而 @ApiOperation 注解标注在 Controller 的方法上。

    @Api    // 看这里,看这里,看这里
    ...
    public class UserController {
    
        @ApiOperation(value = "获取用户信息",   // 看这里,看这里,看这里
                      notes = "根据 id 获取用户信息", 
                      response = Result.class) 
        ...
        public Result<UserVo> getUser(@PathVariable int id) {
            ...
        }
    }
    
  • @ApiImplicitParams 注解和 @ApiImplicitParam 注解用指定 Controller 的非对象参数,即,参数绑定到简单类型时使用。

    ...
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name="username", value = "用户名", type = "String", required = true, paramType = "query", example = "tommy"),
            @ApiImplicitParam(name="password", value = "密码", type = "String", required = true, paramType = "query", example = "123456")
    })
    public Map<String, Object> login(@RequestParam("username") String username,
                                     @RequestParam("password") String password){
        ...
    }
    

    @ApiImplicitParam 的 name 属性要和 @RequestParam 或 @PathVariable 的 value 遥相呼应。

    另外,@ApiImplicitParam 注解的 paramType 属性是用于描述参数是以何种方式传递到 Controller 的,它的值常见有: path, query, body 和 header 。

    • path 表示参数是『嵌在』路径中的,它和 @PathVariable 参数注解遥相呼应;
    • query 表示参数是以 query string 的形式传递到后台的(无论是 get 请求携带在 url 中,还是 post 请求携带在请求体中),它和 @RequestParam 参数注解遥相呼应;
    • body 表示参数是以 json string 的形式携带在请求体中传递到后台的,它和 @RequestBody 参数注解要想呼应;
    • header 表示参数是『藏在』请求头中传递到后台的,它和 @RequestHeader 参数注解遥相呼应的。
    • form 不常用。
  • @ApiModel 注解和 @ApiModelProperty 注解用于指定 Controller 的对象参数,即,参数绑定到 Java Bean 时使用。

    注意

    它俩是配合 @RequestBody 使用。在不配合 @RequestBody 的情况下,它们标注的 java bean 中的值来源据 url 中的 query string,而非请求体中。

    • 提供两个URI 接口作对比

      @PostMapping("/test3")
      public void test3(@RequestBody Student student) {
          log.info(student.toString());
      }
      
    • 自定义用于参数绑定的实体类

      @ApiModel
      public class Student {
      
          @ApiModelProperty(value="主键")
          private String id;
      
          @ApiModelProperty(value="姓名")
          private String name;
      
          @ApiModelProperty(value="年龄")
          private Integer age;
      
          @ApiModelProperty(value="性别")
          private String gender;
      
          ...
      }
      
  • @ApiResponses 注解和 @ApiResponse 标注在 Controller 的方法上,用来描述 HTTP 请求的响应。

# 5. knife4j-Swagger

knife4j 是 Swagger 生成 API 文档的增强解决方案,最主要是 knife4j 提供了动态字段注释功能来实现 Map 来接收参数这个的接口文档生成,忽略参数属性来实现同一个实体类对不同接口生成不同的文档。

# 配置

  • 引入 jar 包

    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>3.0.2</version>
    </dependency>
    
  • 添加注释来开启 knife4j

    @Configuration
    @EnableOpenApi 
    @Import(BeanValidatorPluginsConfiguration.class)
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            ...
        }
    
        private ApiInfo apiInfo() {    
        }
    

    完整的实例如下:

    @Configuration
    @EnableOpenApi 
    @Import(BeanValidatorPluginsConfiguration.class)
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("xxx.yyy.zzz"))
                .paths(PathSelectors.any())
                .build();
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                .title("test")
                .description("接口")
                .termsOfServiceUrl("http://localhost:8080/doc.html")
                .version("1.0")
                .build();
        }
    
    }
    
  • 如果遇到了访问 404,那么进行静态资源拦截配置

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("doc.html")
                    .addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    }
    

knife4j 有提供 UI 来显示,默认访问地址是:

http://${host}:${port}/doc.html

# 忽略参数属性

  • 提供两个 URI 接口作对比

    @PostMapping("/test3")
    public void test3(@RequestBody Student student) {
        log.info(student.toString());
    }
    
    @PostMapping("/test4")
    @ApiOperationSupport(ignoreParameters = {"id"}) // 看这里,看这里,看这里
    public void test4(@RequestBody Student student) {
        log.info(student.toString());
    }
    
  • 自定义用于参数绑定的实体类

    @ApiModel
    public class Student {
    
        @ApiModelProperty(value="主键")
        private String id;
    
        @ApiModelProperty(value="姓名")
        private String name;
    
        @ApiModelProperty(value="年龄")
        private Integer age;
    
        @ApiModelProperty(value="性别")
        private String gender;
    
        ...
    }
    

自动生成文档如下:

knife4j-03

knife4j-04