【Swagger】Swagger入门和一些常见的问题
什么是Swagger
swagger(丝袜哥)是当下比较流行的实时接口文档生成工具。前后端分离后,前后端交流需要一个重要的工具——接口文档。
接口文旦分为:离线文档和实时文档。离线文档,最大的弊端就是接口程序发生变动的时候,需要回过头来维护上面的内容,确实比较麻烦。
实时接口文档可以根据代码来自动生成相应的接口文档。根据代码自动生成的文档,最大的弊端是代码入侵性强(但对比起实时维护接口的麻烦,这是小问题)。Swagger就是其中比较有影响力的实时接口文档。
注:接下来的环境和方法,指的是在Java的SpringBoot框架下,使用和配置Swagger.
配置步骤
swagger的官网地址为:https://swagger.io/ ,当然是英文版的,可以参考。Swagger分为Swagger2和Swagger3两个常用版本。用起来,主体的区别不是很大,现在以Swagger3为例。
1. 在xml下添加配置
Springboot2.6之前的版本(如果高于这个会有版本冲突,解决方案详见下文)
<!-- Swagger 3 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
但是,如果引用Springdoc,注解与spirngfox不同 。引用如下:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi</artifactId>
<version>1.6.9</version>
</dependency>
或者
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> </dependency>
注释对照关系如下:
springfox | springdoc |
@Api |
@Tag
|
@ApiIgnore
|
@Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
|
@ApiImplicitParam
|
@Parameter
|
ApiImplicitParams
|
@Parameters
|
@ApiModel
|
@Schema
|
@ApiModelProperty(hidden = true)
|
@Schema(accessMode = READ_ONLY)
|
@ApiOperation(value = "foo", notes = "bar")
|
@Operation(summary = "foo", description = "bar")
|
@ApiParam
|
@Parameter
|
@ApiResponse(code = 404, message = "foo")
|
@ApiResponse(responseCode = "404", description = "foo") |
Swagger 配置类
/** * Swagger配置类 * @Author:lyj * @Package:com.example.demo.demo.config * @Project:demo * @name:SwaggerConfig * @Date:2024/9/18 14:19 * @Filename:SwaggerConfig */ @Configuration @EnableOpenApi public class SwaggerConfig { /** * 创建API * @return */ @Bean public Docket createRestApi(){ return new Docket(DocumentationType.OAS_30) // 是否启用Swagger .enable(true) // API的基本信息,展示在文档页面中(自定义展示信息) .apiInfo(apiInfo()) // 设置哪些接口暴露给Swaager展示 .select() // 扫码所有注解的api,用这种方式比较灵活 .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 扫描指定包中的swagger注解 //.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) // 扫描所有 .apis(RequestHandlerSelectors.any()) .build(); } /** * 添加照耀信息 * @return */ private ApiInfo apiInfo() { return new ApiInfoBuilder() // 标题 .title("XXXX项目接口文档") // 说明信息 .description("项目的说明信息") // 文档生成的主页地址 .termsOfServiceUrl("文档生成的主页地址") // 作者信息 .contact(new Contact("姓名","url","邮箱地址")) //版本 .version("版本号:1.0") .build(); } }
如果启动成功,Swagger的访问地址:IP地址:端口号/swagger-ui/index.html (http://localhost:8081/swagger-ui/index.html)。
假设已有接口文档
@Data @ApiModel("用户") public class User { private Integer id; @ApiModelProperty("姓名") private String name; @ApiModelProperty("地址") private String addr; }
@RestController @RequestMapping("/user") @Api(value = "用户接口",tags = {"用户接口"}) public class UserController { @ApiOperation("新增用户") @PostMapping("save") public String save(@RequestBody User user){ return "success " + user.getName(); } @ApiOperation("根据条件查询") @GetMapping("get/{id}") public User getById(@PathVariable("id") Integer id){ User user = new User(); user.setId(id); user.setName("zs"); user.setAddr("地址:123456……"); return user; } }
访问成功
SpringBoot和Swagger版本兼容问题
按照以上的方式,当我们启动项目是,可能会报错:“Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException”。
异常原因是:因为SpringBoot和Swagger版本不兼容。
最直接的方法,是将SpringBoot版本,修改到2.6.0以下。如果项目中的SpringBoot版本不能修改的话,我们还可以在application.yml配置文件中进行修改
spring:
profiles:
active: dev
mvc:
pathmatch:
matching-strategy: ant_path_matcher
修改UI
不过,依照我们的习惯,Swagger原生的样式不是特别好看(不是指样式,指的不太好看到接口)。
- 缺乏搜索功能
- 接口类多起来,找接口有如大海捞针。
- 接口边上,没有带着接口注释
- 看Model,需要拖拽到最后,没有很自然的切换。
所有又有一些大神,提供了其他的UI测试页面。这个页面的使用还是比较广泛的。添加引用 (具体引用,根据SpringBoot版本:https://doc.xiaominfo.com/docs/quick-start/start-knife4j-version)
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.0.0</version> </dependency>
接口分组
假设对demo1和demo2两个包进行分组。
额外创建分组1和分组2,Swagger的包或路径过滤,请参考:https://www.cnblogs.com/luyj00436/p/18421728
/** * 默认分组 * @return */ @Bean public Docket createRestApi(){ return new Docket(DocumentationType.OAS_30) // API的基本信息,展示在文档页面中(自定义展示信息) .apiInfo(apiInfo()) // 设置哪些接口暴露给Swaager展示 .select() // 扫码所有注解的api,用这种方式比较灵活 .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 扫描指定包中的swagger注解 //.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) // 扫描所有 .apis(RequestHandlerSelectors.any()) .build(); } /** * 分组1 * @return */ @Bean public Docket ApiDemo1(){ return new Docket(DocumentationType.OAS_30) .groupName("分组1") // API的基本信息,展示在文档页面中(自定义展示信息) .apiInfo(apiInfo1()) // 设置哪些接口暴露给Swaager展示 .select() .apis(RequestHandlerSelectors.basePackage("com.example.demo1")) .build(); } /** * 分组2 * @return */ @Bean public Docket ApiDemo2(){ return new Docket(DocumentationType.OAS_30) .groupName("分组2") // API的基本信息,展示在文档页面中(自定义展示信息) .apiInfo(apiInfo2()) // 设置哪些接口暴露给Swaager展示 .select() .apis(RequestHandlerSelectors.basePackage("com.example.demo2")) .build(); }
swaggerUI 拦截器和跨域冲突处理
如果我们的项目中有关于跨域的处理,同时还有拦截器,然后还要使用swagger,这种情况大家要注意了,有可能我们的拦截器会将swagger中的页面路径拦截掉导致swagger页面出不来,当我们在拦截器中把swagger的页面排除掉的时候,也有可能会导致跨域配置的失效。
第一步,定义拦截器
/** * 拦截器配置 * */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Bean public TokenInterceptor tokenInterceptor() { return new TokenInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry .addInterceptor(tokenInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/user/login") .excludePathPatterns("/user/downloadExcel") .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } }
第2步:跨域配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.setAllowCredentials(true); config.addAllowedMethod("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); configSource.registerCorsConfiguration("/**", config); return new CorsFilter(configSource); } }