Swagger
1. 介紹
swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
1.1 Swagger 組件
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
组件 | 作用 |
---|---|
Swagger Editor | REST APIs文档生成工具 |
Swagger Codegen | 根据编辑完成的Swagger API文档文件生成代码的工具 |
Swagger UI | API文档可视化工具 |
Swagger Inspector | API测试工具 |
1.2 OAS、Swagger 和 Springfox
首先有人定义了一个API文档产生的规范,前期叫Swagger,后面贡献给了Linux基金会,逐渐演变成OpenAPI Specification,逐渐成为世界级别的规范,即OAS,它定义了一种JSON格式或者YAML格式的API文档文件格式;而Swagger提供了Swagger Editor来帮助开发人员熟悉和编写正确的符合OpenAPI规范的API文档文件,这个文件可以使JSON格式的,也可以是YAML格式的;Swagger又提供了Swagger Codegen来生成API文档对应的代码,Swagger Codegen来展示API文档,Swagger Inspector来测试API对应的代码;最后Springfox结合Spring与Swagger,帮助开发人员自动生成对应代码的对应API。
1.3 学习文档
2. SpringBoot 集成 swagger 2.9
2.1 依赖包
<!-- API UI界面 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- 核心依赖 代码html生成等 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
2.2 配置类
@Configuration
@EnableSwagger2
public class Swagger2 {
/**
* 创建API应用
* apiInfo() 增加API相关信息
* 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
* 本例采用指定扫描的包路径来定义指定要建立API的目录。
*
* @return
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.swaggerTest.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 创建该API的基本信息(这些基本信息会展现在文档页面中)
* 访问地址:http://项目实际地址/swagger-ui.html
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot中使用Swagger2构建RESTful APIs")
.description("更多请关注http://www.baidu.com")
.termsOfServiceUrl("http://www.baidu.com")
.contact("sunf")
.version("1.0")
.build();
}
}
2.3 访问
http://locahost:8080/swagger-ui.html
3. SpringBoot 集成 swagger 3.0
3.1 依赖包
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
3.2 配置
配置和swagger2.9一样
// @EnableSwagger2 改为 @EnableOpenApi 即可
3.3 访问
http://locahost:8080/swagger-ui/index.html
3.4 生产关闭
springfox:
documentation:
swagger-ui:
enabled: false
springfox.documentation.swagger-ui.enabled=false
4. Swagger 配置
4.1 文档说明信息
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//设置文档的标题
.title("单词计数服务")
// 设置文档的描述
.description("单词计数服务 API 接口文档")
// 设置文档的版本信息-> 1.0.0 Version information
.version(VERSION)
// 设置文档的 License 信息 ->1.3 License information
.termsOfServiceUrl("http://www.baidu.com")
.build();
}
4.2 文档接口全局属性配置
/**
* swagger 全局属性配置
* @return
*/
@Bean
public Docket createRestApi() {
// base-path处理
if (properties.getBasePath().isEmpty()) {
properties.getBasePath().add(BASE_PATH);
}
// exclude-path处理
if (properties.getExcludePath().isEmpty()) {
properties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
}
ApiSelectorBuilder build = new Docket(DocumentationType.OAS_30)
// 定义是否开启swagger
.enable(properties.getEnabled())
// 设置swagger的调试地址
.host(properties.getHost())
// 设置文档信息
.apiInfo(apiInfo())
// 设置作为API 文档发布的接口
.select()
// 扫描接口的 包路径
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()));
// 设置要扫描加入文档的api
properties.getBasePath().forEach(p -> build.paths(PathSelectors.ant(p)));
// 设置要忽略不加入文档的api
properties.getExcludePath().forEach(p -> build.paths(PathSelectors.ant(p).negate()));
Docket docket = build.build();
// 授权信息设置,必要的header token等认证信息
docket.securitySchemes(security());
// 设置支持通讯协议的集合
HashSet<String> t = new HashSet<String>();
t.add("http");
t.add("https");
docket.protocols(t);
// 设置全局的 接口通用参数信息 请求头 请求体 参数配置
// docket.globalRequestParameters(setRequestParameter());
return docket;
}
4.1 Security 的swagger配置
swagger2
@Override
public void configure ( WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/swagger-ui.html")
.antMatchers("/v2/**")
.antMatchers("/swagger-resources/**");
}
swagger3
//例如
String[] SWAGGER_WHITELIST = {
"/swagger-ui.html",
"/swagger-ui/*",
"/swagger-resources/**",
"/v2/api-docs",
"/v3/api-docs",
"/webjars/**"
};
httpSecurity.antMatchers(SWAGGER_WHITELIST).permitAll();
或
@Override
public void configure(WebSecurity web) throws Exception {
// swagger3 不需要鉴权
web.ignoring().antMatchers(SWAGGER_WHITELIST);
}
4.2 认证信息配置
4.2.1 配置全局认证
docket.securitySchemes(security());
private List<SecurityScheme> security() {
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
return Collections.singletonList(apiKey);
}
配置全局请求头,header 中的 Authorization 如图1打开配置框,2进行value设置,3在下面的请求 try it out 时就会自动带上了,
// 对应的接口需要添加这个属性
@ApiOperation(authorizations = @Authorization(value = "Authorization"))
即在后端可以接收到header中的Authorization属性值。
4.2.2 每个请求都配置
docket.globalRequestParameters(globalRequestParameters())
private List<RequestParameter> globalRequestParameters() {
RequestParameterBuilder parameterBuilder = new RequestParameterBuilder()
//每次请求加载header
.in(ParameterType.HEADER)
//头标签
.name("Token")
.required(false)
.query(param -> param.model(model -> model.scalarModel(ScalarType.STRING)));
return Collections.singletonList(parameterBuilder.build());
}
5. 注解的使用
@Api
用在类上,修饰整个类,描述Controller的作用。
@Api(value = "dict", tags = "字典管理模块")
@RestController
@RequestMapping("/dict")
public class DictController {
}
@ApiOperation
@ApiOperation:描述一个类的一个方法,或者说一个接口。
参数 | 描述 | 举例 |
---|---|---|
value | 接口名称 | value=“test测试接口” |
notes | 接口详细描述 | notes=“应用于测试的一个接口” |
tags | 给接口分组,打标签 | tags = {“测试接口”} |
response | 接口返回参数类型,会在models 中描述该返回对象信息,还会有example value | response = TestController.class |
responseContainer | 声明包装响应的容器。有效值为 List,Set,Map,任何其它值都将被忽略 | |
responseReference | 指定对响应类型的引用,本地/远程引用,并将覆盖任何其它指定的response()类 | |
httpMethod | 接口请求方式,请求方式:“GET”, “HEAD”, “POST”, “PUT”, “DELETE”, “OPTIONS” and “PATCH”,如果未指定则使用除"PATH"之外的其它所有 | httpMethod=“GET” |
position | ||
nickname | 第三方工具使用operationId来唯一表示此操作 | |
produces | 返回参数类型,可以使用, 隔开 | produces = MediaType.APPLICATION_JSON_VALUE |
consumes | 采用逗号分隔的 content types 类型的入参数据类型,例如:application/json,application/xml | |
protocols | 指定协议类型:http,https,ws,wss,多个协议使用逗号进行分隔 | |
authorizations | 认证信息对应value设置 | authorizations = @Authorization(value = “Authorization”) |
hidden | 是否隐藏操作列表中的操作 | |
responseHeaders | 指定 response header 信息列表 | @ResponseHeader(name = “”, response = Void.class) |
code | http 返回状态码 | |
extensions | 可选的扩展数组 | @Extension(properties = @ExtensionProperty(name = “”, value = “”)) |
ignoreJsonView | 解析操作和类型时忽略JsonView注释。用于向后兼容性 |
@ApiOperation(
value="test测试接口",
httpMethod = "GET",
notes="接口发布说明",
tags = {"测试接口"},
response = TestController.class,
authorizations = @Authorization(value = "Authorization"))
@GetMapping("/test")
@ResponseBody
public TestController test(String name, HttpServletRequest request) {
request.getHeader("token");
this.name = name;
return this;
}
@Authorization
接口授权,不单独使用,作为 @Api 或 @ApiOperation 的属性使用
参数 | 描述 | 举例 |
---|---|---|
value | 授权参数的名称 比如Authorization | @ApiOperation(uthorizations = @Authorization(value = “Authorization”)) |
scopes | 授权方案为OAuth2时使用的范围 | @AuthorizationScope(scope = “”, description = “”) |
@AuthorizationScope
接口授权范围使用,不单独使用,作为 @Authorization 的属性使用
参数 | 描述 | 举例 |
---|---|---|
scope | 要使用的 OAuth2 授权方案的范围。范围应事先在Swagger对象的securityDefinition部分中声明 | @AuthorizationScope(scope = “”, description = “”) |
description | 在1.5.X中使用,保留用于旧版本的支持 |
@ApiImplicitParam
描述一个请求参数,可以配置参数的中文含义,还可以给参数设置默认值。
参数:
·paramType:指定参数放在哪个地方
··header:请求参数放置于Request Header,使用@RequestHeader获取
··query:请求参数放置于请求地址,使用@RequestParam获取
··path:(用于restful接口)–>请求参数的获取:@PathVariable
··body:(不常用)
··form(不常用)
·name:参数名
·dataType:参数类型
·required:参数是否必须传(true | false)
·value:说明参数的意思
·defaultValue:参数的默认值
@ApiImplicitParams
描述由多个 @ApiImplicitParam 注解的参数组成的请求参数列表
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "AgentService ID", required = true, dataType = "String"),
@ApiImplicitParam(name = "search", value = "关键字", required = false, dataType = "String"),
@ApiImplicitParam(name = "msgtype", value = "分类,text/file/image/voice", required = false, dataType = "String"),
@ApiImplicitParam(name = "page", value = "请求页数,默认为第一页", required = false, dataType = "String"),
@ApiImplicitParam(name = "limit", value = "每页显示结果数量,默认为20条", required = false, dataType = "String")
})
@ApiParam
单个参数描述
public R listPage(
@ApiParam(value = "当前页码", required = true) @PathVariable Long page,
@ApiParam(value = "每页记录数", required = true) @PathVariable Long limit,
@ApiParam("讲师列表查询对象") TeacherQueryVo teacherQueryVo) {
}
@ApiProperty
用对象接收参数时,描述对象的一个字段
@ApiModel
描述一个Model(用对象来接受参数)的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
@ApiModel(value = "dictVo", description = "字典信息")
public class DictVo implements BaseConvertEntity<Dict> {
}
@ApiModelProperty
用在请求参数为对象时,对象的字段上,可以描述哪些属性不被显示,属性的说明,那些属性时必须的等信息
属性 | 描述 |
---|---|
value | 字段说明 |
name | 重写属性名字 |
dataType | 重写属性类型 |
requied | 是否必填 |
example | 举例说明 |
hidden | 隐藏 |
@ApiModelProperty(value = "备注信息", example = "")
private String remark;
@ApiResponse
HTTP响应其中1个描述,用在@ApiResponses中,一般用于表达一个错误的响应信息
——code:数字,例如400
——message:信息,例如"请求参数异常!"
——response:抛出异常的类
@ApiResponses
描述接口响应
@ApiResponses(value = {
@ApiResponse(code = 405, message = "Invalid input", response = Void.class) })
@ApiIgnore
忽略接口方法
@JsonIgnore
忽略对象字段,请求参数是对象时,该注解可以忽略对象字段不作为请求参数。坑是输出的话,该字段也没有了。
@ApiError
发生错误返回的信息
swagger2 -> swagger3
@Api → @Tag
@ApiIgnore→@Parameter(hidden = true)或@Operation(hidden = true)或@Hidden
@ApiImplicitParam → @Parameter
@ApiImplicitParams → @Parameters
@ApiModel → @Schema
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty → @Schema
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiParam → @Parameter
@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
6. knife4j
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案
集成步骤
前题是swagger已经集成进去了。
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
// 启动类上面添加即可
@EnableKnife4j
页面展示
7. 参考文献
X. 问题记录
版本兼容导致的问题
在使用 springBoot2.7.4 和 Swagger 3 时报错
note-2023-05-16 09:38:14.984 [main] ERROR SpringApplication.java - Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.23.jar:5.3.23]
解决方法:
# 配置文件添加相关配置
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
// swagger 添加配置信息
@Bean
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
WebEndpointsSupplier webEndpointsSupplier,
ServletEndpointsSupplier servletEndpointsSupplier,
ControllerEndpointsSupplier controllerEndpointsSupplier,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties,
Environment environment) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
String basePath = webEndpointProperties.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
return webEndpointProperties.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
}