目录
1. @ApiModel与@ApiModelProperty 实体描述
4. @ApiImplicitParams与@ApiImplicitParam 参数描述
一. Swagger概述
尤其在当前前后端分离的大趋势下,编写和维护开发接口的文档是每个程序员必要的职责。Swagger 首先是一个规范、完整和统一的接口文档维护规范/标准。在这个标准下,Swagger官方提供了很多基于该标准的自动化接口维护工具,用于生成、描述、调用和可视化接口文档的Web 服务。该工具主要包括以下三个部分:
- Swagger Codegen:通过Codegen 可以将描述文件生成html格式和cwiki形式的接口文档,同时也能生成多种语言的服务端和客户端的代码。支持通过jar包,docker,node等方式在本地化执行生成。也可以在后面的Swagger Editor中在线生成。
- Swagger UI:提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。
- Swagger Editor:类似于markendown编辑器的编辑Swagger描述文件的编辑器,该编辑支持实时预览描述文件的更新效果。也提供了在线编辑器和本地部署编辑器两种方式。
在SpringBoot中,由于Swagger的大流行,Spring 基于Swagger 的标准/规范,开发了一套适用于Spring生态的接口工具框架 Springfox-swagger ,可以将基于SpringMVC和Spring Boot项目的项目代码,按照Swagger规范自动生成JSON格式的描述文件并进行展示。Swagger发展到今天,已经有2.x和3.x两种主流的大版本,同样Springfox-swagger也有两种相应的版本,两种版本略有不同,我们这里主要讲Swagger3的整合与使用。
二. SpringBoot整合Swagger3环境搭建
1. 引入maven依赖
springfox-boot-starter 该starter包含了一些swagger必要的自动配置类和启动器,其主要包括:
- springfox-swagger:swagger核心,生成接口文档的Json格式数据
- springfox-swagger-ui:swagger可视化,将Json数据可视化展示
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
2. YML配置
spring boot 2.6.x或更高版本集成Swagger时,直接运行会出现异常信息报错(空指针),主要原因是:Spring Boot 2.6及 更高版本使用的默认路径匹配规则是PathPatternMatcher,而Springfox使用的路径匹配是基于AntPathMatcher的。所以这里需要更改一下SpringBoot配置。
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
3. Swagger配置类
配置类中各部分配置的含义都已经通过注释说明了,这里主要介绍RequestHandlerSelectors和PathSelectors的接口扫描与展示过滤规则,其中:
- RequestHandlerSelectors:
- any():扫描项目所有包下的所有接口
- none():不扫描接口
- withMethodAnnotation:通过方法上的指定注解扫描接口
- withClassAnnotation:通过类上的指定注解扫描接口
- basePackage:根据包路径扫描接口
- PathSelectors:
- any() :任何接口路径Url都扫描展示
- none():任何接口路径Url都不扫描展示
- regex:通过正则表达式控制接口路径Url扫描展示
- ant:通过 ant 路径匹配控制接口路径Url扫描展示
@Configuration //声明配置类
@EnableOpenApi //开启swagger支持
public class SwaggerConfig {
/**
* Docket类是Swagger的配置类,要自定义修改Swagger的默认配置信息,我们需要覆盖该对象
* @return
*/
@Bean
public Docket docket(){
//1.以OAS_30标准构建Docket配置类
return new Docket(DocumentationType.OAS_30)
//2.配置Swagger接口文档基本信息apiInfo
.apiInfo(apiInfo())
//3.select方法开启配置扫描接口的Builder
.select()
//4.指定要扫描/维护接口文档的包(否则就全部扫描)
.apis(RequestHandlerSelectors.basePackage("com.example.bootswagger.controller"))
//5.路径过滤:该Docket-UI展示时,只展示指定路径下的接口文档(any表示都展示)
.paths(PathSelectors.any())
.build();
}
/**
* 配置 Swagger 接口文档的基本信息
* @return
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
//1.接口文档标题
.title("SpringBoot整合Swagger")
//2.接口文档描述内容
.description("这里是SpringBoot整合Swagger的详细信息......,包括...")
//3.项目文档迭代版本
.version("9.0")
//4.主要联系人信息(姓名name,个人主页url,邮箱email)
.contact(new Contact("阿安","www.baidu.com","1436218372@qq.com"))
//5.相关许可证信息
.license("The CSDN License")
//6.相关许可证链接
.licenseUrl("www.baidu.com")
//7.返回构建的ApiInfo对象
.build();
}
}
配置完成后启动SpringBoot项目,访问 http://localhost:8080/swagger-ui/index.html 或 http://localhost:8080/swagger-ui/ 即可看到生成的接口文档。
4. Swagger分组配置
在实际开发中,可能是是由很多人来共同开发一组接口功能,每个人负责不同的部分。那么对于每个人/每个小组负责的文档部分也应该进行分组,使得开发过程更加清晰。在Swagger实现中,一个Docket配置类对应一组文档配置,既然要进行分组,那么分几组我们就要分别配置几个相应的Docket对象,然后通过groupName()方法配置分组信息(名称)。
@Configuration //声明配置类
@EnableOpenApi //开启swagger支持
public class SwaggerConfig {
/**
* 配置分组文档 docketA :该部分文档由王五负责,主要涉及UserController相应接口的开发,对应url为 /user/**
* @return
*/
@Bean
public Docket docketA(){
//1.以OAS_30文档标准构建Docket配置类
return new Docket(DocumentationType.OAS_30)
//2.配置Swagger接口文档基本信息apiInfoA
.apiInfo(apiInfoA())
//3.配置文档分组-名称
.groupName("docketA")
//4.select方法开启配置扫描接口的Builder
.select()
//5.指定要扫描/维护接口文档的包:bootswagger.controller
.apis(RequestHandlerSelectors.basePackage("com.example.bootswagger.controller"))
//6.指定路径过滤规则:docketA只展示 /user/** 路径下的接口描述(配合包扫描)
.paths(PathSelectors.ant("/user/**"))
.build();
}
/**
* 配置分组文档 docketB :该部分文档由李四负责,主要涉及AdminController相应接口的开发,对应url为 /admin/**
* @return
*/
@Bean
public Docket docketB(){
//1.以OAS_30文档标准构建Docket配置类
return new Docket(DocumentationType.OAS_30)
//2.配置Swagger接口文档基本信息apiInfoB
.apiInfo(apiInfoB())
//3.配置文档分组-名称
.groupName("docketB")
//4.select方法开启配置扫描接口的Builder
.select()
//5.指定要扫描/维护接口文档的包:bootswagger.controller
.apis(RequestHandlerSelectors.basePackage("com.example.bootswagger.controller"))
//6.指定路径过滤规则:docketA只展示 /user/** 路径下的接口描述(配合包扫描)
.paths(PathSelectors.ant("/admin/**"))
.build();
}
/**
* 配置 文档A 的基本信息
* @return
*/
private ApiInfo apiInfoA(){
return new ApiInfoBuilder()
//1.接口文档标题
.title("文档A:SpringBoot整合Swagger")
//2.接口文档描述内容
.description("这里是SpringBoot整合Swagger的详细信息......,包括...")
//3.项目文档迭代版本
.version("9.0")
//4.主要联系人信息(姓名name,个人主页url,邮箱email)
.contact(new Contact("王五","www.baidu.com","1436218372@qq.com"))
//7.返回构建的ApiInfo对象
.build();
}
/**
* 配置 文档B 的基本信息
* @return
*/
private ApiInfo apiInfoB(){
return new ApiInfoBuilder()
//1.接口文档标题
.title("文档B:SpringBoot整合Swagger")
//2.接口文档描述内容
.description("这里是SpringBoot整合Swagger的详细信息......,包括...")
//3.项目文档迭代版本
.version("2.0")
//4.主要联系人信息(姓名name,个人主页url,邮箱email)
.contact(new Contact("李四","www.baidu.com","1436218372@qq.com"))
//7.返回构建的ApiInfo对象
.build();
}
}
三. Swagger 常用注解配置
1. @ApiModel与@ApiModelProperty 实体描述
- @ApiModel(value="实体类名称",description="实体类描述"):标注在实体类上,用于对实体类进行描述说明
- @ApiModelProperty(value="简要描述",hidden=false,dataType="数据类型"):标注于实体类属性或方法上,用于对实体类的属性或方法进行描述说明。其中hidden表示是否隐藏该属性(默认为false)
@ApiModel(description = "此实体类封装了返回值结果,包括状态码与返回值消息...")
public class ResultVo {
@ApiModelProperty(value = "状态码",hidden = false,dataType = "Integer")
private Integer code;
@ApiModelProperty(value = "响应消息",hidden = false,dataType = "String")
private String message;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "ResultVo{" +
"code=" + code +
", message='" + message + '\'' +
'}';
}
}
注意:接口方法所涉及的实体类(出现在接口方法的返回值、参数上等方式)都会自动被扫描并插入到接口文档中。所以并不是因为@ApiModel这个注解让实体类显示,而@ApiModel和@ApiModelProperty这两个注解只是为显示的实体添加注释说明的。
2. @Api 接口模块类描述
- @Api(tags = "接口模块类描述说明"):该注解标注在接口模块类上,用于对接口模块类进行描述说明
@RestController
@RequestMapping("/user")
@Api(tags = "用户请求处理Controller")
public class UserController {
}
3. @ApiOperation 接口方法描述
- @ApiOperation(value="接口方法简要说明",notes="接口方法详细描述",response=Class<T>):该注解标注在接口模块类的接口方法上,用于对接口方法进行描述说明。其中notes字段可以插入HTML标签,response字段表示该方法的返回值类型。
@RestController
@RequestMapping("/user")
@Api(tags = "用户请求处理Controller")
public class UserController {
@ApiOperation(value = "用户登陆方法",notes = "<span style='color:red;'>描述:</span> 用来执行用户登录方法的接口",response = ResultVo.class)
@RequestMapping(value = "/login",method = RequestMethod.GET)
public ResultVo loginUser(){
ResultVo resultVo = new ResultVo();
resultVo.setCode(1);
resultVo.setMessage("Login SUCCESS");
return resultVo;
}
@ApiOperation(value = "用户注册方法",notes = "<span style='color:red;'>描述:</span> 用来执行用户注册方法的接口",response = ResultVo.class)
@RequestMapping(value = "/register",method = RequestMethod.GET)
public ResultVo registerUser(){
ResultVo resultVo = new ResultVo();
resultVo.setCode(2);
resultVo.setMessage("Register SUCCESS");
return resultVo;
}
}
4. @ApiImplicitParams与@ApiImplicitParam 参数描述
- @ApiImplicitParams(list=[ ]):该注解作用在接口方法上,是ApiImplicitParam的列表
- @ApiImplicitParam(name="参数名称",value = "参数说明",defaultValue = "参数默认值",dataType = "数据类型"):该注解作用于ApiImplicitParams列表内,用于对方法上的参数进行描述说明。
@RestController
@RequestMapping("/user")
@Api(tags = "用户请求处理Controller")
public class UserController {
@ApiOperation(value = "用户登陆方法",notes = "<span style='color:red;'>描述:</span> 用来执行用户登录方法的接口",response = ResultVo.class)
@RequestMapping(value = "/login",method = RequestMethod.GET)
@ApiImplicitParams({
@ApiImplicitParam(name="username",value = "用户名",defaultValue = "admin",dataType = "String"),
@ApiImplicitParam(name="userInfo",value = "用户信息",defaultValue = "nothing",dataType = "String")
})
public ResultVo loginUser(String userName,String userInfo){
System.out.println(userName);
System.out.println(userInfo);
ResultVo resultVo = new ResultVo();
resultVo.setCode(1);
resultVo.setMessage("Login SUCCESS");
return resultVo;
}
}
注意:参数使用@RequestBody时,@ApiImplicitParam的type就不要使用body了,二者相互冲突,可以使用 @ApiParam 处理。而且不使用@RequestBody时,@ApiImplicitParam将参数统一附加到地址url;使用@RequestBody后,不再附加。
四. Swagger 接口文档导出/更改UI
Swagger原生官方依赖不提供文档导出功能,只能网页在线查看。所以要实现接口文档导出,我们可以通过以下两种方式(注意url拦截器放行):
- 整合第三方Swagger-UI界面提供的文档导出功能(主要)
- bootstrap-UI
- Layui-UI
- mg-UI
- 整合第三方导出插件,实现文档导出功能
1. bootstrap-UI
注意访问地址为:http://localhost:8080/doc.html
<!-- 引入swagger-bootstrap-ui包 访问地址 /doc.html-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
2. knife4j
knife4j是bootstrap-UI的升级版。相比bootstrap-UI来说,knife4j功能更加完善和强大,并且其starter中已经封装了springfox,我们无需再次引入。官网文档地址:https://doc.xiaominfo.com/knife4j/
(1)如果开发者继续使用OpenAPI2的规范结构,底层框架依赖springfox2.10.5版本,那么可以考虑Knife4j
的2.x版本
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.9</version>
</dependency>
(2)如果开发者使用OpenAPI3的结构,底层框架依赖springfox3.0.0,可以考虑Knife4j
的3.x版本
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
(3)除了引入starter不同,其他配置均与之前swagger配置相同。配置完成后,启动项目访问http://localhost:8080/doc.html 即可
五. Swagger 整合常见问题
1. 权限管理中的拦截问题
在Shiro或SpringSecurity权限管理框架中,使用Swagger时访问文档主页会被拦截,因此我们需要配置放行相关swagger资源的访问请求。下面以Shiro整合为例:
/**
* 定义拦截器链,所有请求都经过自定义的jwt过滤器
* @return
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
Map<String,String> filterRuleMap = new LinkedHashMap<>();
//放行swagger资源
filterRuleMap.put("/swagger-ui/**","anon");
filterRuleMap.put("/swagger-resources/**","anon");
filterRuleMap.put("/v3/**","anon");
filterRuleMap.put("/webjars/**","anon");
//jwt统一拦截
filterRuleMap.put("/**","jwt");
definition.addPathDefinitions(filterRuleMap);
return definition;
}