Swagger 的使用

前言

后端时代

  • 前端:只用管理静态页面:html。
  • 后端:模板引擎 JSP,后端是主力

前后端分离时代

主流框架:Vue + SpringBoot

  • 后端:后端控制层,服务层,数据访问层【后端团队】
  • 前端:前端控制层,视图层【前端团队】
    • 伪造后端数据:json假数据。不需要请求后端,仅前端工程已经可以启动并操作。

前后端通过 API 接口进行交互,相对独立,松耦合,可以部署在不同的服务器上

前端后分离存在的问题:

  • 前后端集成联调,前端人员和后端人员无法做到“及时协商,尽早解决”,最终导致问题集中爆发;

解决方案:

  • 首先制定 schema (计划的提纲),试试更新最新 API ,降低集成风险;
  • 早期:制定word计划文档;
  • 前后端分离:
    • 后端接口测试工具:postman
    • 后端向前端提供接口,需要实时更新罪行的消息及改动!

Swagger 简介

  • 号称世界上最流程的 Api 框架;
  • 在线自动生成 RestFul Api 文档,该文档与 Api 接口信息同步更新;
  • 直接运行,可在线测试 Api 接口;
  • 支持多种语言:Java、Php ...

官网:

  • https://swagger.io/

使用:

  • 在项目中使用 Swagger 需要两个 springfox 依赖:
    • springfox-swagger2
    • springfox-swagger-ui

SpringBoot 集成 Swagger 2.x

  1. 新建一个 SpringBoot 项目(web 项目);

  2. 导入依赖

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
        <exclusions>
            <exclusion>
                <!--
                io.springfox:springfox-swagger2:2.9.2中依赖了swagger-models的1.5.20版本,
                通过排除springfox-swagger2中的swagger-models依赖,导入io.swagger:swagger-models的1.5.22版本.
                解决io.swagger.models.parameters.AbstractSerializableParameter实例化参数时example为空字符串""而报错的问题.
                因为1.5.20的example只判断是否为null,1.5.22判断了是否为null和""
                -->
                <groupId>io.swagger</groupId>
                <artifactId>swagger-models</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/io.swagger/swagger-models -->
    <dependency>
        <groupId>io.swagger</groupId>
        <artifactId>swagger-models</artifactId>
        <version>1.5.22</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    
  3. 编写一个 Hello 工程;

  4. 在主程序上添加注解,开启 Swagger2

    @SpringBootApplication
    //开启 Swagger2
    @EnableSwagger2
    public class SwaggerDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SwaggerDemoApplication.class, args);
        }
    
    }
    
  5. 测试运行,访问 Swagger UI 界面

    http://localhost:8080/swagger-ui.html
    

SpringBoot 集成 Swagger 3.0

  1. 新建一个 SpringBoot 项目(web项目);

  2. 导入依赖

    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-boot-starter -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  3. 编写一个 Hello 工程;

  4. 在主程序上添加注解,开启Swagger

    @SpringBootApplication
    //开启 Swagger
    @EnableOpenApi
    public class SwaggerDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SwaggerDemoApplication.class, args);
        }
    
    }
    
  5. 测试运行,访问 Swagger UI 界面 注意:路径同 Swagger 2.x 不同

    http://localhost:8080/swagger-ui/index.html
    

配置 Swagger

配置文档信息

Swagger 的 bean 实例:Docket

Docket.apiInfo(ApiInfo apiInfo)方法

具体配置

@Configuration
public class SwaggerConfig {

    /**
     * 配置 Swagger Docket 的 bean实例
     *
     * @return Docket 的 bean实例
     */
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo());
    }

    /**
     * 配置 swagger 的信息:ApiInfo
     *
     * @return ApiInfo 的 bean实例
     */
    public ApiInfo apiInfo() {

        //作者信息
        Contact contact = new Contact("小聪", "https://blog.csdn.net/weixin_52610802?type=blog", "coder_cong@163.com");

        return new ApiInfo("Aladdin的SwaggerAPI文档",
                "不负韶华,未来可期",
                "V1.0",
                "https://blog.csdn.net/weixin_52610802?type=blog",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }

}

配置扫描接口

Docket.select()方法

具体配置

/**
 * 配置 Swagger Docket 的 bean实例
 *
 * @return Docket 的 bean实例
 */
@Bean
public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            .select()
            /*
            RequestHandlerSelectors 配置扫描接口的方式
                basePackage():指定要扫描的包(通常指定包扫描)
                any():扫描全部
                none():不扫描
                withMethodAnnotation():扫描方法上的注解
                withClassAnnotation():方法类上的注解
             */
            .apis(RequestHandlerSelectors.basePackage("com.aladdin.swagger.controller"))
            /*
            PathSelectors 请求路径匹配的方式
                regex():正则匹配
                any():扫描全部
                none():不扫描
                ant():路径匹配
             */
            .paths(PathSelectors.ant("/aladdin/**"))
            .build();
}

配置功能是否启动

Docket.enable(boolean externallyConfiguredFlag)方法

具体配置

/**
 * 配置 Swagger Docket 的 bean实例
 *
 * @return Docket 的 bean实例
 */
@Bean
public Docket docket() {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            //是否启动 Swagger,如果为 false,则 UI界面提示:【😱 Could not render e, see the console.】,无法在浏览器中访问 Swagger
            .enable(false)
            .select()
         	.apis(RequestHandlerSelectors.basePackage("com.aladdin.swagger.controller"))
            .paths(PathSelectors.ant("/aladdin/**"))
            .build();
}

实用案例

  • 根据服务环境动态配置 Swagger 是否开启。
/**
 * 配置 Swagger Docket 的 bean实例
 *
 * @return Docket 的 bean实例
 */
@Bean
public Docket docket(Environment environment) {

    //配置要显示 Swagger 的环境
    Profiles profiles = Profiles.of("dev", "test");
    //通过environment.acceptsProfiles()方法判断当前是否处在自己设定的环境中
    boolean flag = environment.acceptsProfiles(profiles);

    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            //是否开启 Swagger,如果为 false,则 UI界面提示:【😱 Could not render e, see the console.】,无法在浏览器中访问 Swagger
            .enable(flag)
            .select()
         	.apis(RequestHandlerSelectors.basePackage("com.aladdin.swagger.controller"))
            .paths(PathSelectors.ant("/aladdin/**"))
            .build();
}

配置 API 文档的分组

Docket.groupName(String groupName)方法

具体配置

/**
 * 配置 Swagger Docket 的 bean实例
 *
 * @return Docket 的 bean实例
 */
@Bean
public Docket docket(Environment environment) {
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            .enable(true)
            //设置组名称,一个 Docket bean 对应一个组名称
            .groupName("阿拉丁")
            .apis(RequestHandlerSelectors.basePackage("com.aladdin.swagger.controller"))
            //.paths(PathSelectors.ant("/aladdin/**"))
            .build();
}
配置多个分组

配置多个分组,需配置多个 Docket bean,每个 bean 配置各自的 groupName

package com.aladdin.swagger.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
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 java.util.ArrayList;

@Configuration
public class SwaggerConfig {

    /**
     * 配置 Swagger Docket 的 bean实例
     *
     * @return Docket 的 bean实例
     */
    @Bean
    public Docket docket(Environment environment) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(true)
                .groupName("阿拉丁")
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.aladdin.swagger.controller"))
                //.paths(PathSelectors.ant("/aladdin/**"))
                .build();
    }

    /**
     * 配置 swagger 的信息:ApiInfo
     *
     * @return ApiInfo 的 bean实例
     */
    public ApiInfo apiInfo() {

        //作者信息
        Contact contact = new Contact("阿拉丁", "https://blog.csdn.net/weixin_52610802?type=blog", "coder_cong@163.com");

        return new ApiInfo("Aladdin的SwaggerAPI文档",
                "不负韶华,未来可期",
                "V1.0",
                "https://blog.csdn.net/weixin_52610802?type=blog",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }

    @Bean
    public Docket docket1() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("分组1");
    }

    @Bean
    public Docket docket2() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("分组2");
    }

    @Bean
    public Docket docket3() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("分组3");
    }

}

模型类模块(Models)

  • Swagger 页面 Models 模块为实体类信息

  • 如果 Swagger 分组扫描的接口返回值存在实体类,则此实体类就会被扫描到该分组中

    1. 新建实体类

      public class User {
      
          private String username;
          private String password;
      
          ...
      }
      
      
    2. 增加返回值为 User 类的接口

      @RestController
      public class HelloController {
      
          @GetMapping("/hello")
          public String hello() {
              return "Hello";
          }
      
          @PostMapping("/user")
          public User user() {
              return new User("123", "qwe");
          }
      
      }
      
    3. 重启项目,访问 Swagger 页面,则会看见 Models 中加载了 User 实体类

文档注释

可使用 Swagger 注解为生成的 Api 在线文档添加注释信息

作用范围API使用位置
描述返回对象的意义@ApiModel用在返回对象类上
对象属性@ApiModelProperty用在出入参数对象的字段上
作用范围API使用位置
协议集描述@Api用于controller类上
协议描述@ApiOperation用在controller的方法上
Response集@ApiResponses用在controller的方法上
Response@ApiResponse用在 @ApiResponses里边
非对象参数集@ApiImplicitParams用在controller的方法上
非对象参数描述@ApiImplicitParam用在@ApiImplicitParams的方法里边
  • @Api:用在controller上,对controller进行注释;

  • @ApiOperation:用在API方法上,对该API做注释,说明API的作用;

  • @ApiResponses:通常用来包含接口的一组响应注解,可以简单的理解为响应注解的集合声明;

  • @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息;

    • code:即httpCode,例如400;
    • message:信息,例如"请求参数没填好";

    即使只有一个@ApiResponse,也需要使用@ApiResponses包住。

  • @ApiImplicitParams:用来包含API的一组参数注解,可以简单的理解为参数注解的集合声明;

  • @ApiImplicitParam:用在 @ApiImplicitParams 注解中,也可以单独使用,说明一个请求参数的各个方面,该注解包含的常用选项有:

    属性取值作用
    paramType查询参数类型
    path以地址的形式提交数据
    query直接跟参数完成自动映射赋值
    body以流的形式提交 仅支持POST
    header参数在request headers 里边提交
    form以form表单的形式提交,仅支持POST
    dataType参数的数据类型 只作为标志说明,并没有实际验证
    Long
    String
    name接收参数名
    value接收参数的意义描述
    required参数是否必填
    true必填
    false非必填
    defaultValue默认值代码
    1. form域中的值需要使用@RequestParam获取
    2. header域中的值需要使用@RequestHeader来获取
    3. path域中的值需要使用@PathVariable来获取
    4. body域中的值使用@RequestBody来获取,否则可能出错;而且如果paramType是body,name就不能是body,否则有问题,与官方文档中的 “If paramType is "body", the name should be "body" 不符。

Swagger Demo


部分示范代码

package com.aladdin.swagger.pojo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

@ApiModel("用户实体类")
public class User {

    @ApiModelProperty(value = "用户名", example = "123123", required = true)
    private String username;

    @ApiModelProperty(value = "密码", example = "aaa123", required = true)
    private String password;

    ...
}
package com.aladdin.swagger.controller;

import com.aladdin.swagger.pojo.User;
import io.swagger.annotations.*;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

@Api("Hello 控制类")
@RestController
public class HelloController {

    @ApiOperation("测试query")
    @ApiResponses({@ApiResponse(code = 1000, message = "操作成功"),
            @ApiResponse(code = 9999, message = "系统异常"),
            @ApiResponse(code = 8888, message = "权限不足")})
    @ApiImplicitParams({@ApiImplicitParam(paramType = "query", dataType = "String", name = "str", value = "字符串", required = true)})
    @GetMapping("/hello")
    public String hello(String str) {
        return "Hello: " + str;
    }

    @ApiOperation("测试path")
    @ApiImplicitParams({@ApiImplicitParam(paramType = "path", dataType = "Long", name = "id", value = "信息id", required = true)})
    @GetMapping("/hello2/{id}")
    public String hello2(@PathVariable("id") Long id) {
        return "Hello: " + id;
    }

    @ApiOperation("测试header")
    @ApiImplicitParams({@ApiImplicitParam(paramType = "header", dataType = "Long", name = "id", value = "信息id", required = true)})
    @GetMapping("/hello3")
    public String hello3(@RequestHeader("id") Long id) {
        return "Hello: " + id;
    }

    @ApiOperation("测试form")
    @ApiImplicitParams({ @ApiImplicitParam(paramType = "form", dataType = "Long", name = "id", value = "信息id", required = true) })
    @PostMapping(value = "/hello4", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public String hello4(@RequestParam("id") Long id) {
        return "Hello: " + id;
    }

    @ApiOperation("测试body")
    @ApiImplicitParams({@ApiImplicitParam(paramType = "body", dataType = "User", name = "user", value = "用户信息参数", required = true)})
    @PostMapping("/user")
    public User user(@RequestBody User user) {
        return user;
    }

}

总结

  1. 可通过 Swagger 给一些比较难理解的属性或者接口增加注释信息;
  2. 接口文档实时更新;
  3. 可以在线测试。

注意:出于安全考虑,在正式发布的时候,关闭 Swagger,同时也会节省运行内存。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐