1、Swagger简介
Swagger 是一个规范和完整的框架,用于生成、描述、功能调用测试和可视化 RESTful 风格的在线的接口文档工具。
Swagger 是一套基于 OpenAPI 规范(OpenAPI Specification,OAS)构建的开源工具。
Swagger 提供了一套通过代码和注解自动生成可视化的 RESTful 风格的API文档,符合 RESTful API设计的行业标准。
OAS本身是一个API规范,它用于描述一整套API接口,包括一个接口是哪种请求方式、哪些参数、哪些header等,都会被包括在这个文件中。它在设计的时候通常是YAML格式,这种格式书写起来比较方便,而在网络中传输时又会以json形式居多,因为json的通用性比较强。
2、Swagger主要提供了几种开源工具
- Swagger Codegen:通过Codegen 可以将描述文件生成html格式和cwiki形式的接口文档,同时也能生成多钟语言的服务端和客户端的代码。支持通过jar包,docker,node等方式在本地化执行生成。也可以在后面的Swagger Editor中在线生成。
- Swagger UI:提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。
- Swagger Editor:基于浏览器的编辑器,该编辑支持实时预览描述文件的更新效果。也提供了在线编辑器和本地部署编辑器两种方式。
- Swagger Inspector:感觉和postman差不多,是一个可以对接口进行测试的在线版的postman。比在Swagger UI里面做接口请求,会返回更多的信息,也会保存你请求的实际请求参数等数据。
- Swagger Hub:集成了上面所有项目的各个功能,你可以以项目和版本为单位,将你的描述文件上传到Swagger Hub中。在Swagger Hub中可以完成上面项目的所有工作,需要注册账号,分免费版和收费版。
3、Springfox介绍
由于 Spring的流行,Marty Pitt编写了一个基于 Spring的组件 swagger-springmvc,用于将 Swagger集成到 SpringMVC中来,而 Springfox则是从这个组件发展而来。
通常 Spring Boot项目整合 Swagger2需要用到两个依赖:springfox-swagger2和springfox-swagger-ui,用于自动生成swagger文档。
- springfox-swagger2:这个组件的功能用于帮助我们自动生成描述API的json文件
- springfox-swagger-ui:就是将描述API的json文件解析出来,用一种更友好的方式呈现出来
官方 UI很多人不太喜欢,觉得不太符合国人的使用习惯。通常我们通过引入 swagger-bootstrap-ui依赖来替换官方 UI。
xml
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
4、Swagger常用注解
更多注解和使用说明,请查看Github:https://github.com/swagger-api/swagger-core/wiki/Annotations
jva
@Api:用在请求的类上,表示对类的说明(不配置默认是按类名驼峰变下划线显示)
value:该参数没什么意义,在UI界面上也看到,所以不需要配置"
tags:说明该类的作用,可以在UI界面上看到的注解
description:对api资源的描述
basePath:基本路径
position:如果配置多个Api 想改变显示的顺序位置
produces:如 "application/json, application/xml"
consumes:如 "application/json, application/xml"
protocols:协议类型,如: http, https, ws, wss.
authorizations:高级特性认证时配置
hidden:配置为true ,将在文档中隐藏
@ApiOperation:用在请求的方法上,说明方法的用途、作用
value:说明方法的用途、作用
tags:如果设置这个值、value的值会被覆盖
notes:方法的备注说明
description:对api资源的描述
@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
· header --> 请求参数的获取:@RequestHeader
· query --> 请求参数的获取:@RequestParam
· path(用于restful接口)--> 请求参数的获取:@PathVariable
· div(不常用)
· form(不常用)
dataType:参数类型,默认String,其它值dataType="Integer"
defaultValue:参数的默认值
@ApiParam() 用于方法,参数,字段说明;表示对参数的添加元数据(说明或是否必填等)
name:参数名
value:参数说明
required:是否必填
@ApiResponses:用在请求的方法上,表示一组响应
@ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
code:数字,例如400
message:信息,例如"请求参数没填好"
response:抛出异常的类
@ApiModel:用于pojo类上,描述一个Model的信息
(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候,比如使用@RequestBody这样的场景)
@ApiModelProperty:用在属性上,描述一个model的属性
@ApiIgnore:注解类、参数、方法,注解后将不在Swagger UI中显示
5、Spring Boot整合Swagger2
SpringBoot 2.3.12.RELEASE版本项目集成 Swagger2的步骤:
- 引入 Swagger2依赖
- 添加 Swagger2配置类
- 项目中给Controller添加Swagger注解
- 访问 Swagger2的UI界面
5.1 引入 Swagger2依赖
xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
5.2 添加 Swagger2配置类
在项目中开启了 Swagger的功能:添加一个@EnableSwagger2注解接口。可以添加在SpringBoot的启动类上或者Swagger2配置类上。
在config文件夹下,添加一个Swagger2Config类。
添加 @EnableSwagger2注解。
使用 @Bean,在启动时初始化,返回实例 Docket(Swagger API 摘要对象)配置相关映射路径和要扫描的接口的位置等信息
java
@EnableSwagger2
@Configuration
public class Swagger2Config {
/**
* 访问官方文档 UI界面:http://127.0.0.1:18080/swagger-ui.html
* <p>
* 增强版 UI界面:http://127.0.0.1:18080/doc.html
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2) // SWAGGER_2
.apiInfo(apiInfo())
.select()
// 此处自行修改为自己的 Controller 包路径
.apis(RequestHandlerSelectors.basePackage("com.charge.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(setGlobalParameters());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("XXX 项目接口文挡")
.description("本文档描述 XXX平项目接口定义")
.version("1.0")
.termsOfServiceUrl("") //文档生成的主页地址
.contact(new Contact("author", null, "xxxx@163.com"))
.build();
}
/**
* 设置全局参数
*
* @return
*/
private List<Parameter> setGlobalParameters() {
List<Parameter> globalParameterList = new ArrayList<>();
//Header中必需 token参数。非必填,传空也可以,一般业务登录拦截器校验 token是否合法
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("token").description("用户 TOKEN参数")
.required(false)// 非必填
.modelRef(new ModelRef("string"))
.parameterType("header");
globalParameterList.add(tokenBuilder.build());
return globalParameterList;
}
}
看具体项目具体配置
java
package com.open.web.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
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 springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket getDocket(){
//创建Docket对象
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.enable(true)
.apiInfo(apiInfo())//指定API接口文件首页信息
.select()//初始化并返回一个API选择构造器
.apis(RequestHandlerSelectors.any())//为任何接口生成API文档
.paths(PathSelectors.any())//可以根据url路径设置哪些请求加入文档,忽略哪些请求
.build(); //创建API文档
return docket;
}
private ApiInfo apiInfo(){
//创建作者 对象
Contact contact = new Contact("尼古拉斯","http://www.baidu.com","123@qq.com");
ApiInfo apiInfo = new ApiInfoBuilder()
.title("后端接口文档》") //文档标题
.description("文档的描述信息...")//文档描述
.contact(contact)//文档作者
.version("V1.0")//文档版本
.build(); //构建
return apiInfo;
}
}
java
@Bean
public Docket createRestApi() {
Predicate<RequestHandler> predicate = new Predicate<RequestHandler>() {
@Override
public boolean test(RequestHandler input) {
Class<?> declaringClass = input.declaringClass();
if (declaringClass == BasicErrorController.class)// 排除
return false;
if (declaringClass.getTypeName().indexOf("com.yubin.cn") <= -1)
return false;
if (declaringClass.isAnnotationPresent(RestController.class)) // 被注解的类
return true;
if (declaringClass.isAnnotationPresent(Controller.class)) // 被注解的类
return true;
if (input.isAnnotatedWith(ResponseBody.class)) // 被注解的方法
return true;
return false;
}
};
return new Docket(DocumentationType.SWAGGER_2).enable(swaggerSwitch).apiInfo(apiInfo())
.useDefaultResponseMessages(false).select().apis(predicate).build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("接口服务")// 大标题
.version("1.0.0").description("V1.0.0 初始版本\r\n")// 版本
.build();
}
5.3 给Controller添加Swagger注解
(1)controller
ags:tags不同,表示UI的主页下的接口归类目录不同。
java
@RestController
@RequestMapping("/sys/user")
@Api(value = "SysUserController", tags = {"用户管理相关操作接口"})
public class SysUserController {
@Autowired
private SysUserService sysUserService;
@PostMapping("/pageQuery")
@ApiOperation(value = "用户分页查询接口", notes = "notes用户分页查询接口")
public WebResult<BasePageResult<SysUserDTO>> pageQuery(@RequestBody SysUserPageQueryRequest pageQueryRequest) {
BasePageResult basePageResult = sysUserService.pageQuery(pageQueryRequest);
return WebResult.ok(basePageResult);
}
@DeleteMapping("/{id}")
@ApiOperation("根据id删除用户的接口")
@ApiImplicitParam(name = "id", value = "用户id", dataType = "Integer", defaultValue = "10", required = true)
@ApiResponses({
@ApiResponse(code = 200, message = "删除成功"),
@ApiResponse(code = 500, message = "删除成功")
})
public void delete(@PathVariable Integer id) {
System.out.println("删除成功:" + id);
}
@PutMapping("/update/{id}")
@ApiOperation(value = "更新用户", notes = "根据用户id更新用户名接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "用户id", dataType = "Integer", defaultValue = "10", required = true),
@ApiImplicitParam(name = "username", value = "用户名", dataType = "String", defaultValue = "lisi", required = true)
})
public User update(@PathVariable Integer id, String username) {
User user = new User();
user.setId(id);
user.setUsername(username);
return user;
}
}
(2)实体类
java
@Data
@ApiModel("系统用户分页查询请求体")
public class SysUserPageQueryRequest extends BasePageRequest {
//@Serial
private static final long serialVersionUID = -7586253215056854970L;
@ApiModelProperty("创建时间开始")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTimeStart;
@ApiModelProperty("创建时间结束")
private Date createTimeEnd;
}
5.4 访问 Swagger2的UI界面
启动项目。Swagger2官方UI界面访问地址:http://ip:port/项目路径/swagger-ui.html
5.5 替换官方 UI
我们引入 swagger-bootstrap-ui依赖,移除官方 UI依赖。
xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.springfox</groupId>-->
<!-- <artifactId>springfox-swagger-ui</artifactId>-->
<!-- <version>2.9.2</version>-->
<!-- </dependency>-->
<!-- 常用 UI界面依赖 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
重新启动项目。替换官方 UI界面之后,新 UI界面访问地址:http://ip:port/项目路径/doc.html
5.6 在项目中,我们通过不同的包名来管理不同的 controller,达到对接口分层的目的。对我们查找接口非常方便。
Swagger也支持对 API分组展示的功能。其实很简单,我们只需要重新定义一个 Docket的 bean,在其中指定另外接口层的包路径即可。通过 groupName设置组名,默认是default。
注意:Docket和 Swagger注解中 tags的区别:
- Docket:对不同接口层的包路径进行分组。
- @Api(tags = {"用户管理相关操作接口"}):表示同一个组下,对请求接口类中方法的归类管理。
这里简单点,重新复制一个 Docket的配置信息,其他信息一样。重启项目访问 UI。
java
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2) // SWAGGER_2
.groupName("后端管理平台")
.apiInfo(apiInfo())
.select()
// 此处自行修改为自己的 Controller 包路径
.apis(RequestHandlerSelectors.basePackage("com.charge.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(setGlobalParameters());
}
@Bean
public Docket createRestApi2() {
return new Docket(DocumentationType.SWAGGER_2) // SWAGGER_2
.groupName("后端管理平台2")
.apiInfo(apiInfo())
.select()
// 此处自行修改为自己的 Controller 包路径
.apis(RequestHandlerSelectors.basePackage("com.charge.xxx.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(setGlobalParameters());
}
6、项目使用拦截器之后配置调整
项目中我们经常会添加拦截器,所以需要对 Swagger相关资源进行放行。
通常添加一个 WebMvcConfig配置类,在里面添加一下 Swagger相关配置即可
6.1 添加登录拦截器
java
@Component
@Slf4j
public class LoginTokenInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("LoginTokenInterceptor -> preHandle 请求方式={},url = {}, ", request.getMethod(), request.getRequestURI());
String token = request.getHeader("token");
if (StringUtils.isBlank(token)) {
token = request.getParameter("token");
}
if (StringUtils.isBlank(token)) {
log.info("token 为空");
}
log.info("token = {}", token);
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("LoginTokenInterceptor -> postHandle 请求方式={},url = {}, ", request.getMethod(), request.getRequestURI());
super.postHandle(request, response, handler, modelAndView);
}
}
6.2 WebMvcConfig配置类
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginTokenInterceptor loginTokenInterceptor;
/**
* 配置登录拦截器
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTokenInterceptor)
.addPathPatterns("/**")
// Swagger2放行。 /doc.html/**为新UI路径放行。
.excludePathPatterns("/swagger-resources/**", "/webjars/**",
"/v2/**", "/swagger-ui.html/**", "/doc.html/**");
}
/**
* 配置静态资源访问拦截
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
// swagger-bootstrap-ui依赖
registry.addResourceHandler("doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
}
/**
* 配置 CORS跨域问题
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600);
}
}
重新启动项目。新 UI界面访问地址:http://ip:port/项目路径/doc.html。测试接口OK。