前言
在目前主流的前后端分离项目中,桥接前后端的就是接口,因此,一份简洁易懂的接口文档就显得非常重要了。幸运的是,我们不必手动去编写这些接口文档,市面上已有许多成熟的第三方库可以自动为项目生成接口文档,在 Spring Boot 中,最常使用的接口文档自动生成库就是 Swagger。
本篇博文主要介绍在 Spring Boot 中集成 Swagger 3 的方法。
几个概念释义
-
OpenAPI
:OpenAPI
规范(以前也被称为Swagger
规范)制定了一份标准的对 REST API 接口的描述格式,具体的描述信息包含如下几方面:- 可用终端接口(
/user
)及其各接口对应的操作(GET /user, POST /user
) - 每个操作的输入、输出参数描述
- 认证方法
- 联系方式,许可证,使用条款等其他信息
API规范 可以使用
YAML
或JSON
格式进行描述,对于人和机器来说都是阅读友好的。 - 可用终端接口(
-
Swagger
:Swagger
是围绕OpenAPI
规范构建的一系列开源工具,可以帮助我们设计、构建、记录和使用 REST API。主要的工具包含如下:-
Swagger Editor:可以在浏览器上编辑
OpenAPI
规范。 -
Swagger UI:将
OpenAPI
规范渲染成可交互 API 接口文档。 -
Swagger Codegen:依据
OpenAPI
规范文件生成服务器存根和客户端库。
-
Swagger Editor:可以在浏览器上编辑
更多详细信息,请参考官网:Specification
简单来说,OpenAPI
就是定义描述 REST API 的规范,而Swagger
就是对OpenAPI
规范的实现。
基本使用
在 Spring Boot 中集成 Swagger 3,步骤如下:
-
在
pom.xml
中导入依赖:<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version> </dependency>
-
依赖导入完成后,直接在浏览器输入以下网址:
http://localhost:8080/swagger-ui/
此时就可以直接看到项目接口文档了。
-
(可选)Swagger 3 提供了一些注解,可以让我们自定义接口描述信息。相关的注解如下表所示:
注:Swagger 3 提供的注解与 Swagger2 名称不同,下表列举了其对应关系:
Swagger 2 Swagger 3 (OpenAPI 3) 注解位置 @Api
@Tag(name = "接口类描述")
Controller
类上@ApiOperation
@Operation(summary = "接口方法描述")
Controller
方法上@ApiImplicitParams
@Parameters
Controller
方法上@ApiImplicitParam
@Parameter(description=“参数描述”)
@Paramseters
内部@ApiParam
@Parameter(description=“参数描述”)
Controller
方法参数上@ApiIgnore
@Parameter(hidden = true)
或@Operation(hidden = true)
或@Hidden
- @ApiModel
@Schema
DTO 类上 @ApiModelProperty
@Schema
DTO 属性上 一个简单的示例如下所示:
@RestController @RequestMapping("/user") @Tag(name = "用户接口") public class UserApi { @PostMapping @Operation(summary = "添加用户") public String addUser(@RequestBody User user) { //... } @GetMapping("/all") @Operation(description = "获取所有用户") public List<User> getAllUsers() { //... } }
可以看到,Swagger 3 的使用还是非常简单的。
自定义配置
如果需要更细致的自定义配置,我们可以自定义一个配置类,然后注入一个Docket
数据实例,自定义配置 Swagger。
以下列举几种常见的配置示例:
-
配置文档相关信息:
@Configuration @ConditionalOnProperty(value = "springfox.documentation.enabled", havingValue = "true", matchIfMissing = true) public class SwaggerConfig { @Bean public Docket docket(){ return new Docket(DocumentationType.OAS_30) // 配置接口相关信息 .apiInfo(apiInfo()) // 选择哪些接口作为 swagger 的 doc 发布 .select() .build(); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("XX项目接口文档") .description("XX项目描述") .contact(new Contact("作者", "作者URL", "作者Email")) .version("1.0") .build(); } }
-
配置包扫描路径:
@Bean public Docket docket(){ return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .select() //apis: 添加swagger接口提取范围 // 指定扫描包 .apis(RequestHandlerSelectors.basePackage("com.yn.controller")) // 指定扫描方法注解 // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 所有路径 .paths(PathSelectors.any()) .build(); }
-
Api 分组:默认情况下,Swagger 将扫描到的接口都归属于
default
组,我们可以通过对每个接口路径(/public
、/admin
)各自配置一个Docket
,显式设置其分组:@Bean public Docket publicApi() { return new Docket(DocumentationType.OAS_30) .groupName("api-public") .select() .apis(RequestHandlerSelectors.basePackage("com.yn.controller.open")) // localhost:8080/public .paths(PathSelectors.regex("/public.*")) .build(); } @Bean public Docket privateApi() { return new Docket(DocumentationType.OAS_30) .groupName("api-admin") .select() .apis(RequestHandlerSelectors.basePackage("com.yn.controller.admin")) .paths(PathSelectors.regex("/admin.*")) .build(); }
-
请求头添加 token:越来越多的项目采用前后端分离架构,此时经常使用 token 来作为鉴权机制,因此前端每次请求都必须携带上 token,我们可以配置下 Swagger,让其每次请求自动携带我们设置的 token,只需对相应
Docket
进行配置即可,如下所示:@Bean public Docket addJwtToken() { return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()) .securitySchemes(Collections.singletonList(HttpAuthenticationScheme.JWT_BEARER_BUILDER // 显示用 .name("JWT") .build())) .securityContexts(Collections.singletonList(SecurityContext.builder() .securityReferences(Collections.singletonList(SecurityReference.builder() .scopes(new AuthorizationScope[0]) .reference("JWT") .build())) // 声明作用域 .operationSelector(o -> o.requestMappingPattern().matches("/.*")) .build())) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); }
配置完成后,打开该
Docker
配置对应的页面,就可以看到Authorize
的按钮,如下图所示:
点击该按钮,手动输入 token,最后点击
Authorize
按钮确认即可,如下图所示:
Swagger 更多配置选项,请参考官方文档:springfox
其他注意事项
Swagger 在使用过程中,有一些事项可以注意一下,避免出现问题。比如:
-
区分环境:通常情况下,Swagger 只在开发环境或测试环境下开启,在生产环境下必须进行关闭。可以通过在配置文件
application.properties
中配置使能或失能 Swagger:# 生产环境失能 Swagger springfox.documentation.enabled=false # 或者 springfox.documentation.swagger-ui.enabled=false
注:在
springfox-boot-starter.jar
包中,我们可以找到/META-INF/spring.factories
配置文件,其内容如下:# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration
可以看到,Swagger 的自动配置文件类为
OpenApiAutoConfiguration
,其源码如下所示:@Configuration @EnableConfigurationProperties({SpringfoxConfigurationProperties.class}) @ConditionalOnProperty( value = {"springfox.documentation.enabled"}, havingValue = "true", matchIfMissing = true ) @Import({OpenApiDocumentationConfiguration.class, SpringDataRestConfiguration.class, BeanValidatorPluginsConfiguration.class, Swagger2DocumentationConfiguration.class, SwaggerUiWebFluxConfiguration.class, SwaggerUiWebMvcConfiguration.class}) @AutoConfigureAfter({WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class}) public class OpenApiAutoConfiguration { public OpenApiAutoConfiguration() { } }
这里我们主要关注
@ConditionalOnProperty
,可以看到,当springfox.documentation.enabled
设置为true
(缺省默认也为true
)时,才会加载OpenApiAutoConfiguration
自动配置类。因此,这里也是为什么我们前面自定义配置类SwaggerConfig
也带上这个配置,这样可以确保在springfox.documentation.enabled=false
时,我们的自定义配置类SwaggerConfig
也不会被加载。注:Spring Boot 多环境配置方法可参考文章:Spring Boot - 多环境配置
-
安全框架放行:如果项目中使用了 Spring Security 这种权限认证框架,应当注意要把 Swagger 添加到白名单中:
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { String[] SWAGGER_WHITELIST = { "/swagger-ui.html", "/swagger-ui/*", "/swagger-resources/**", "/v2/api-docs", "/v3/api-docs", "/webjars/**" }; web.ignoring().antMatchers(SWAGGER_WHITELIST); } }
-
统一数据下发异常:如果项目中使用了
@RestControllerAdvice
拦截所有接口请求,那么 Swagger 的内置接口也同样会被拦截修改,导致Unable to infer base url
错误,此时,只需为@RestControllerAdvice
设置拦截指定包目录即可,如下所示:// 只拦截 com.yn.controller 包 @RestControllerAdvice(basePackages = {"com.yn.controller"}) public class FormatResponseBodyAdvice implements ResponseBodyAdvice<Object> {...}
-
@EnableWebMvc
异常:当项目中使用了@EnableWebMvc
注解后,会导致 Swagger 无法访问。原因是在 Spring Boot 中,注解在配置类(即@Configuration
)类上的@EnableWebMvc
会完全掌控 SpringMVC 配置,导致 Spring Boot 对 SpringMVC 的自动配置失效(即不加载自动配置),从而间接影响到了 Swagger。解决该问题的方法有很多,这里列举几种:-
移除
@EnableWebMvc
:对于 Spring Boot 项目,通常不建议在配置类(@Configuration
)上直接使用@EnableWebMvc
,很多对 SpringMVC 的配置,可以直接搜索 Spring Boot 对应的配置选项即可,大多数情况下无需直接配置原生 SpringMVC。 -
添加视图解析器
ViewResolver
:使用@EnableWebMvc
后,SpringMVC 中,内置的视图解析器无法对 Swagger 的视图进行解析,因此手动为DispatcherServlet
添加 Spring 内置的视图解析器即可:
@Configuration @EnableWebMvc public class WebConfiguration implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.viewResolver(new InternalResourceViewResolver()); } }
注:可单步调式
DispatcherServlet
,在DispatcherServlet#resolveViewName
方法内就是对视图进行解析的过程,此处可查看到DispatcherServlet
具体所使用到的ViewResolver
。 -
移除