Swagger 规范已于 2015 年捐赠给 Linux 基金会后改名为 OpenAPI,并定义最新的规范为 OpenAPI 3.0。所以现在的 Swagger 3.0 就是 OpenAPI 3.0。跟Swagger2差不多,只是将@EnableSwagger2换成@EnableOpenAPI
knife4j 是一个Swagger的增强工具,能够完善项目的接口文档。
SpringFox是Spring社区维护的一个项目(非官方),帮助使用者将Swagger2集成到Spring中。常常用于Spring中帮助开发者生成文档,并可以轻松的在Spring Boot中使用。
SpringDoc也是Spring社区维护的一个项目(非官方),帮助使用者将Swagger3集成到Spring中。
Swagger官网:https://swagger.io/
SpringFox项目:https://github.com/springfox/springfox
SpringDoc项目:https://github.com/springdoc/springdoc-openapi
SpringDoc文档:https://springdoc.org/
访问地址:openapi3: localhost:8080//swagger-ui/index.html
引入依赖
<!-- springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
配置
在Spring Boot 应用程序中,通常不需要额外配置,Springdoc会自动扫描控制器和模型。可以在 application.properties或application.yml中进行一些基本配置,例如:
# Docs API配置
springdoc:
swagger-ui:
# API文档, 默认路径: /swagger-ui/index.html
path: /swagger-ui/index.html
# 开启Swagger UI界面
enabled: true
# 根据HTTP方法对API路径进行排序
operations-sorter: method
api-docs:
# OpenAPI的路径描述,默认路径:/v3/api-docs
# OpenAPI描述定义默认为JSON格式, 通过http://localhost:8080/v3/api-docs.yaml获取yaml格式
path: /v3/api-docs
# 开启api-docs
enabled: true
# 配置需要生成接口文档的扫描包路径
packages-to-scan: com.ldk
# 配置需要生成接口文档的接口路径
paths-to-match: /**
可以创建一个配置类来自定义 Springdoc 的行为:设置标题、描述和版本等。还可以对API进行分组:按模块、按功能划分API。
@Configuration
public class OpenApiConfig {
/**
* 配置自定义的 OpenAPI 规范
* 通过 @Bean 注解声明该方法返回一个 Spring Bean,该 Bean 是一个 OpenAPI 对象
* 该方法允许通过 Spring Context 初始化 OpenAPI 对象,并自定义 API 的标题、版本、描述等信息
*
* @return 自定义的 OpenAPI 对象
*/
@Bean
public OpenAPI customOpenAPI() {
// 创建并配置 OpenAPI 对象
return new OpenAPI()
.info(new Info()
.title("我的 API") // 设置 API 标题
.version("v0.0.1") // 设置 API 版本
.description("这是一个示例 API") // 设置 API 描述
.license(new License().name("Apache 2.0").url("http://example.com")) // 设置 API 的许可证信息,包括许可证名称和 URL
.contact(new Contact().name("开发者姓名").url("http://example.com").email("developer@example.com"))) // 设置联系人的姓名 url地址,邮箱
.externalDocs(new ExternalDocumentation().description("外部文档的描述").url("http://example.com")) // 设置外部文档的描述 设置外部文档的 URL
}
创建controller
@Tag(name = "测试API接口", description = "这是一个关于测试API接口的描述")
@RequestMapping("/test")
@RestController
public class HelloController {
@Operation(summary = "获取问候信息", description = "返回一个简单的问候信息")
@ApiResponse(responseCode = "200", description = "成功")
@GetMapping("/hello")
public String hello() {
return "Hello, World!";
}
}
访问:localhost:8080//swagger-ui/index.html
设置分组
配置方式设置分组
# Docs API配置
springdoc:
swagger-ui:
path: /swagger-ui/index.html
enabled: true
group-configs:
- group: '测试1'
paths-to-match: /test/**
配置类中设置分组
@Bean
public GroupedOpenApi testApi() {
return GroupedOpenApi.builder()
.group("test") // 设置组名
.pathsToMatch("/test/**") // 设置需要匹配的路径模式
.build();
}
常用注解
| 注解 | 描述 |
|---|---|
@Tag | 为一组 API 操作添加标签,便于在文档中组织和分组。 |
@Operation | 描述一个 API 操作,包括摘要和详细描述。 |
@Parameter | 描述方法参数,包括路径变量、请求体和查询参数。 |
@Schema | 描述数据模型的属性和结构,通常用于模型类或 API 方法的参数和返回值。 |
@ApiResponse | 描述单个 HTTP 响应状态码的详细信息。 |
@ApiResponses | 描述多个 HTTP 响应状态码的详细信息。 |
@RequestBody | 指定请求体的内容类型和结构。 |
@Content | 描述响应内容的类型和格式。 |
@SecurityRequirement | 描述 API 操作所需的安全要求,例如认证和授权。 |
@Hidden | 指定某个 API 操作或模型在文档中隐藏。 |
@Deprecated | 表示某个 API 操作或模型已被弃用。 |
@ArraySchema | 描述数组类型的响应内容,通常用于返回列表。 |
@ExampleObject | 提供示例对象,用于 API 文档中展示请求或响应的示例。 |
@MediaType | 指定请求或响应的媒体类型。 |
@Link | 描述 API 之间的链接关系。 |
@ParameterObject | 描述复合参数对象,通常用于请求体中的复杂结构。 |
左边时swagger2,右边时openapi3的注解
@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")
详细示例
创建一个更详细、简单的图书管理API REST控制器示例,展示如何使用Springdoc 生成 API 文档的同时,处理不同的 HTTP 方法和请求参数。
创建模型类
首先,定义一个图书模型类 Book:
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "图书信息")
public class Book {
@Schema(description = "图书ID", example = "1")
private Long id;
@Schema(description = "图书标题", example = "Spring Boot 入门")
private String title;
@Schema(description = "图书作者", example = "张三")
private String author;
}
创建REST控制器
接下来,创建一个 BookController 控制器,提供 CRUD 操作:
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Tag(name = "图书管理API接口", description = "这是一个关于图书管理API接口的描述")
@RestController
@RequestMapping("/books")
public class BookController {
private final List<Book> books = new ArrayList<>();
@Operation(summary = "获取所有图书", description = "返回图书列表", method = "GET")
@ApiResponse(responseCode = "200", description = "成功", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Book.class)))
@GetMapping
public List<Book> getAllBooks() {
return books;
}
@Operation(summary = "根据ID获取图书", description = "通过图书ID获取图书信息", method = "GET")
@ApiResponse(responseCode = "200", description = "成功")
@ApiResponse(responseCode = "404", description = "未找到图书")
@GetMapping("/{id}")
public Book getBookById(@Parameter(description = "图书ID", required = true) @PathVariable Long id) {
return books.stream()
.filter(book -> book.getId().equals(id))
.findFirst()
.orElse(null);
}
@Operation(summary = "添加新图书", description = "创建新的图书记录", method = "POST")
@ApiResponse(responseCode = "201", description = "图书创建成功")
@PostMapping
public Book createBook(@Parameter(description = "图书信息", required = true) @RequestBody Book book) {
books.add(book);
return book;
}
@Operation(summary = "更新图书信息", description = "根据ID更新图书信息", method = "PUT")
@ApiResponse(responseCode = "200", description = "图书更新成功")
@ApiResponse(responseCode = "404", description = "未找到图书")
@PutMapping("/{id}")
public Book updateBook(@Parameter(description = "图书ID", required = true) @PathVariable Long id,
@Parameter(description = "图书信息", required = true) @RequestBody Book updatedBook) {
for (int i = 0; i < books.size(); i++) {
if (books.get(i).getId().equals(id)) {
books.set(i, updatedBook);
return updatedBook;
}
}
return null; // 或者抛出异常
}
@Operation(summary = "删除图书", description = "根据ID删除图书", method = "DELETE")
@ApiResponse(responseCode = "204", description = "图书删除成功")
@ApiResponse(responseCode = "404", description = "未找到图书")
@DeleteMapping("/{id}")
public void deleteBook(@Parameter(description = "图书ID", required = true) @PathVariable Long id) {
books.removeIf(book -> book.getId().equals(id));
}
}
安全策略
在OpenAPI规范中,安全策略类型(SecurityScheme.Type)用于定义API的认证机制。SpringDoc通过SecurityScheme类来配置这些认证机制。
SecuritySchemeType类:
public enum SecuritySchemeType {
DEFAULT(""),
APIKEY("apiKey"),
HTTP("http"),
OPENIDCONNECT("openIdConnect"),
OAUTH2("oauth2");
}
用于基于HTTP的认证机制,如Basic Auth或Bearer Token。
例如,Bearer Token认证用于JWT令牌认证。
@Bean
public OpenAPI customOpenAPI() {
// 创建并配置 OpenAPI 对象
return new OpenAPI()
.info(new Info()
.title("我的 API") // 设置 API 标题
.version("v0.0.1") // 设置 API 版本
.description("这是一个示例 API") // 设置 API 描述
.license(new License().name("Apache 2.0").url("http://example.com")) // 设置 API 的许可证信息,包括许可证名称和 URL
.contact(new Contact().name("开发者姓名").url("http://example.com").email("developer@example.com"))) // 设置联系人的姓名 url地址,邮箱
.externalDocs(new ExternalDocumentation().description("外部文档的描述").url("http://example.com")) // 设置外部文档的描述 设置外部文档的 URL
.components(new io.swagger.v3.oas.models.Components()
.addSecuritySchemes(HttpHeaders.AUTHORIZATION,
new SecurityScheme().type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name(HttpHeaders.AUTHORIZATION)));
}
请求会加入Authorization请求头,值为:Bearer a.b.c:
curl -X GET -H "Accept:*/*"
-H "Authorization:Bearer a.b.c"
-H "Content-Type:application/x-www-form-urlencoded" "http://localhost:8080/test/hello"
全局接口设置 Authorization
@Bean
public GlobalOpenApiCustomizer globalOpenApiCustomizer() {
return openApi -> {
// 全局添加鉴权参数
if (openApi.getPaths() != null) {
openApi.getPaths().forEach((url, pathItem) -> {
//注册登录不用认证
if(url.equals("/login") || url.equals("/register")){
return;
}
// 接口添加鉴权参数
pathItem.readOperations().forEach(operation ->
operation.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
);
});
}
};
}
集成网关
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", matchIfMissing = true)
public class SpringDocConfiguration implements InitializingBean {
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
private final DiscoveryClient discoveryClient;
/**
* 在初始化后调用的方法,用于注册SwaggerDocRegister订阅器
*/
@Override
public void afterPropertiesSet() {
SwaggerDocRegister swaggerDocRegister = new SwaggerDocRegister(swaggerUiConfigProperties, discoveryClient);
// 手动调用一次,避免监听事件掉线问题
swaggerDocRegister.onEvent(null);
NotifyCenter.registerSubscriber(swaggerDocRegister);
}
}
/**
* Swagger文档注册器,继承自Subscriber<InstancesChangeEvent>
*/
@RequiredArgsConstructor
class SwaggerDocRegister extends Subscriber<InstancesChangeEvent> {
private final SwaggerUiConfigProperties swaggerUiConfigProperties;
private final DiscoveryClient discoveryClient;
/**
* 事件回调方法,处理InstancesChangeEvent事件
* @param event 事件对象
*/
@Override
public void onEvent(InstancesChangeEvent event) {
Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> swaggerUrlSet = discoveryClient.getServices()
.stream()
.flatMap(serviceId -> discoveryClient.getInstances(serviceId).stream())
.filter(instance -> StringUtils.isNotBlank(instance.getMetadata().get("spring-doc")))
.map(instance -> {
AbstractSwaggerUiConfigProperties.SwaggerUrl swaggerUrl = new AbstractSwaggerUiConfigProperties.SwaggerUrl();
swaggerUrl.setName(instance.getServiceId());
swaggerUrl.setUrl(String.format("/%s/v3/api-docs", instance.getMetadata().get("spring-doc")));
return swaggerUrl;
})
.collect(Collectors.toSet());
swaggerUiConfigProperties.setUrls(swaggerUrlSet);
}
/**
* 订阅类型方法,返回订阅的事件类型
* @return 订阅的事件类型
*/
@Override
public Class<? extends Event> subscribeType() {
return InstancesChangeEvent.class;
}
}
SpringDoc:一个用于自动生成API文档的工具SpringDoc是一个用于Spring Boot的库,可以帮助生成 - 掘金 (juejin.cn)
knife4j
springboot2 openapi3 springdoc
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
配置:
# Docs API配置
springdoc:
swagger-ui:
# API文档, 默认路径: /swagger-ui/index.html
path: /swagger-ui/index.html
# 开启Swagger UI界面
enabled: true
# 根据HTTP方法对API路径进行排序
operations-sorter: method
api-docs:
# OpenAPI的路径描述,默认路径:/v3/api-docs
# OpenAPI描述定义默认为JSON格式, 通过http://localhost:8080/v3/api-docs.yaml获取yaml格式
path: /v3/api-docs
# 开启api-docs
enabled: true
# 配置需要生成接口文档的扫描包路径
packages-to-scan: com.ldk
# 配置需要生成接口文档的接口路径
paths-to-match: /**
knife4j:
enable: true
setting:
language: zh_cn
配置类:
@Configuration
public class OpenApiConfig {
/**
* 配置自定义的 OpenAPI 规范
* 通过 @Bean 注解声明该方法返回一个 Spring Bean,该 Bean 是一个 OpenAPI 对象
* 该方法允许通过 Spring Context 初始化 OpenAPI 对象,并自定义 API 的标题、版本、描述等信息
*
* @return 自定义的 OpenAPI 对象
*/
@Bean
public OpenAPI customOpenAPI() {
// 创建并配置 OpenAPI 对象
return new OpenAPI()
.info(new Info()
.title("我的 API") // 设置 API 标题
.version("v0.0.1") // 设置 API 版本
.description("这是一个示例 API") // 设置 API 描述
.license(new License().name("Apache 2.0").url("http://example.com")) // 设置 API 的许可证信息,包括许可证名称和 URL
.contact(new Contact().name("开发者姓名").url("http://example.com").email("developer@example.com"))) // 设置联系人的姓名 url地址,邮箱
.externalDocs(new ExternalDocumentation().description("外部文档的描述").url("http://example.com")) // 设置外部文档的描述 设置外部文档的 URL
}
}
访问:
注意:使用knife4j时不要使用分组,否则会报错