SpringDoc【使用详解】
SpringDoc使用详解
- 一、何为SpringDoc
- 二、概念解释
- 三、SpringDoc使用
- 2.1简单集成
- 2.2 配置SpringDoc
- 2.2.1 yml方式配置
- 2.2.2配置文档信息
- 2.3配置文档分组
- 2.4使用注解
- 2.4.1 @Tag
- 2.4.2 @Operation
- 2.4.3 @Schema
- 2.4.4 @NotNull
- 2.4.5 @Parameter
- 2.4.6 @Parameters
- 2.4.7 @ApiResponses 和@ApiResponse
- 四、认证授权
- 3.1无需认证
- 3.2需要认证
- 五、SpringDoc整合Knife4j
- 1.前世今生
- 2.Spring Boot版本兼容性
- 3.Spring Boot 3
- 4.Spring Boot 2
一、何为SpringDoc
SpringDoc是一个用来自动生成API文档的库。它是基于SpringBoot项目的,遵循OpenAPI3(一个组织规定的规范)规范。它是通过检查我们运行中的程序,推断出基于Spring配置、类结构和各种注解的API语义,从而自动生成JSON、YAML和HTML格式的接口文档。
而我们不得不提的就是Swagger。Swagger是一个公司的开源项目,将自己的API设计贡献给了OpenAPI并由其标准化。在SpringDoc之前我们还可以使用Springfox,和SpringDoc一样是一个用于生成API文档的库,2020年起不再更新。
SpringDoc官网
二、概念解释
谈到API文档,那就绕不开大名鼎鼎的Swagger,但是你是否还听说过:OpenAPI,Springfox,Springdoc?你第一次看到这些脑瓜子是不是嗡嗡的?
-
OpenAPI
是一个组织(OpenAPI Initiative),他们指定了一个如何描述HTTP API的规范(OpenAPI Specification)。既然是规范,那么谁想实现都可以,只要符合规范即可。 -
Swagger
它是SmartBear这个公司的一个开源项目,里面提供了一系列工具,包括著名的 swagger-ui。swagger是早于OpenApi的,某一天swagger将自己的API设计贡献给了OpenApi,然后由其标准化了。 -
Springfox
是Spring生态的一个开源库,是Swagger与OpenApi规范的具体实现。我们使用它就可以在spring中生成API文档。以前基本上是行业标准,目前最新版本可以支持 Swagger2, Swagger3 以及 OpenAPI3 三种格式。但是其从 2020年7月14号就不再更新了,不支持springboot3,所以业界都在不断的转向我们今天要谈论的另一个库Springdoc,新项目就不要用了。 -
Springdoc
算是后起之秀,带着继任Springfox的使命而来。其支持OpenApi规范,支持Springboot3,我们的新项目都应该使用这个。
三、SpringDoc使用
我们可以在springboot中使用SpringDoc来生成API文档,详情可以参考官网,下面我们来简单的实践一下。
2.1简单集成
在springboot中使用springdoc起步非常容易,只需要引入其starter即可
<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.8.6</version></dependency>
运行后访问下面的链接即可
http://server:port/context-path/swagger-ui.html
例如
http://localhost:8080/swagger-ui.html
例如我们有如下代码:
@RestController
@RequestMapping("/api/programmer")
public class ProgrammerController {@PostMapping()public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {return new Programmer(1, request.getAge(), request.getProgrammingLang());}@GetMapping("/{id}")public Programmer getProgrammer(@PathVariable Integer id) {return new Programmer(1, 35, List.of("Java,Python,SQL"));}
}
添加依赖运行后访问http://localhost:8080/swagger-ui.html后结果如下
是不是特牛逼?当然springdoc的集成不可能就这点东西,不然也没有这篇文章了,咱接着往下看。苦于网上的一些教程不直观,对初学者不友好,所以本文以代码和其效果的形式来展示,保你一看就会。
2.2 配置SpringDoc
2.2.1 yml方式配置
springdoc:api-docs:# 是否开启接口文档enabled: trueswagger-ui:# 持久化认证数据persistAuthorization: trueinfo:# 标题title: '标题:${demo.name}多租户管理系统_接口文档'# 描述description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'# 版本version: '版本号: ${demo.version}'# 作者信息contact:name: Agazigiemail: 123456789@qq.comurl: www.baidu.comcomponents:# 鉴权方式配置security-schemes:apiKey:type: APIKEYin: HEADERname: ${sa-token.token-name}#这里定义了两个分组,可定义多个,也可以不定义group-configs:- group: 1.演示模块packages-to-scan: org.dromara.demo- group: 2.通用模块packages-to-scan: org.dromara.web- group: 3.系统模块packages-to-scan: org.dromara.system- group: 4.代码生成模块packages-to-scan: org.dromara.generator
2.2.2配置文档信息
得益于springboot的强大,我们只需添加一个依赖就可以使用API文档了,但是使用的都是默认值,我们当然也希望对其进行各种自定义的配置
- 配置文档信息
创建一个OpenAPI 的bean,配置文档名称等信息
@Configuration
public class SpringDocConfig {@Beanpublic OpenAPI myOpenAPI() {return new OpenAPI().info(new Info() //设置信息.title("程序员API") //标题.description("程序员的大本营") //描述信息.version("v1.0.0") //版本.license(new License() //许可证.name("许可协议").url("https://shusheng007.top")).contact(new Contact() //作者信息.name("书生007").email("wangben850115@gmail.com"))).externalDocs(new ExternalDocumentation() //外部文档.description("ShuSheng007博客").url("https://shusheng007.top"));}// 指定扫描的包路径@Beanpublic GroupedOpenApi publicApi() {return GroupedOpenApi.builder().group("my_api") // 分组名称(自定义).packagesToScan("com.ls.ai.demo.controller") // 包路径.addOpenApiMethodFilter(methodPredicate()) // 方法级注解过滤.build();}// 定义过滤条件:仅包含带有 @Operation 注解的方法private Predicate<HandlerMethod> methodPredicate() {return handlerMethod ->handlerMethod.hasMethodAnnotation(io.swagger.v3.oas.annotations.Operation.class);}// 可选:过滤类级别的注解(如 @Tag)private Predicate<HandlerMethod> classPredicate() {return handlerMethod ->handlerMethod.getBeanType().isAnnotationPresent(io.swagger.v3.oas.annotations.tags.Tag.class);}}
效果:
里面的配置项很多,可以根据代码和截图对照一下,按照自己的需求配置即可
2.3配置文档分组
用来配置分组的,假如你有两类controller一类以/api为前缀,一类以/admin为前缀,就可以将其配置为两个分组。很多时候我们只有一个分组,所以就不需要下面的配置。
@Configuration
public class SpringDocConfig {...@Beanpublic GroupedOpenApi publicApi() {return GroupedOpenApi.builder().group("api").pathsToMatch("/api/**").build();}@Beanpublic GroupedOpenApi adminApi() {return GroupedOpenApi.builder().group("admin").pathsToMatch("/admin/**").build();}
}
效果:
可以通过右上角的下拉框选择要展示的group。
2.4使用注解
这个是咱们的重头戏,OpenApi
规范提供了很多注解,下面是一些常用的
注解 | 含义 |
---|---|
@Tag | 用在controller类上,描述此controller的信息 |
@Operation | 用在controller的方法里,描述此api的信息 |
@Parameter | 用在controller方法里的参数上,描述参数信息 |
@Parameters | 用在controller方法里的参数上 |
@Schema | 用于Entity,以及Entity的属性上 |
@ApiResponse | 用在controller方法的返回值上 |
@ApiResponses | 用在controller方法的返回值上 |
@Hidden | 用在各种地方,用于隐藏其api |
下面我们一起来看看效果
2.4.1 @Tag
@Tag(name = "程序员", description = "程序员乐园")
@RestController
@RequestMapping("/api/programmer")
public class ProgrammerController {
...
}
效果
2.4.2 @Operation
@Operation(summary = "创建程序员", description = "用于创建一个闷骚的程序员")
@PostMapping()
public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {return new Programmer(666, "王二狗", request.getAge(), request.getProgrammingLang());
}
@Operation
其实很复杂的,我们可以将下面要用的@Parameter
以及@ApiResponse
都可以配置在它里面。
2.4.3 @Schema
@Schema
用于实体类和其属性,例如有如下代码:
@Schema(description = "创建程序员入参")
public class CreateProgrammerRequest {@Schema(description = "名称", example = "王二狗")private String name;@Schema(description = "年龄", example = "35")private Integer age;@Schema(description = "掌握的编程语言", type = "List", example = "[\"Java\",\"Sql\"]")private List<String> programmingLang;
}
效果:
还可以切换到 Schema选项进行查看
2.4.4 @NotNull
同时,Springdoc还支持 Java Bean Validation API 的注解,例如@NotNull
等
@Schema(description = "创建程序员入参")
public class CreateProgrammerRequest {@NotNull@Schema(description = "名称", example = "王二狗")private String name;@NotNull@Min(18)@Max(35)@Schema(description = "年龄", example = "35")private Integer age;...
}
效果:
注意红框中的内容,name和age右上角都有出现了一个红色的星星,表示是必填的。age也被限制了范围。
2.4.5 @Parameter
用于添加接口参数信息
@GetMapping("/{id}")
public Programmer getProgrammer(@Parameter(description = "程序员id") @PathVariable Integer id) {...
}
效果
2.4.6 @Parameters
与@Parameter
作用一样,但是可以批量添加,不用一个一个的写在参数前面
@Parameters(value = {@Parameter(name = "name", description = "姓名", in = ParameterIn.PATH),@Parameter(name = "age", description = "年龄", in = ParameterIn.QUERY)
})
@GetMapping("/{name}")
public List<Programmer> getProgrammers(@PathVariable("name") String name, @RequestParam("age") Integer age) { ...
}
parameters里的parameter使用name来找到方法中的入参,这块要对应上。
效果:
2.4.7 @ApiResponses 和@ApiResponse
顾名思义,此注解用来描述返回值的。
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "成功",content = {@Content(mediaType = "application/json",schema = @Schema(implementation = Programmer.class))}),@ApiResponse(responseCode = "405", description = "非法输入",content = @Content)})@PostMapping()public Programmer createProgrammer(@RequestBody CreateProgrammerRequest request) {...}
效果
可见,我们成功配置了两种情况的返回值。但是我们一般不会手动给每个API 写上一堆@ApiResponse
,那的多烦啊。业界通常会有一个统一的返回类型,例如
public class Result<T> implements Serializable {private int code;private String message;private T data;private String traceId;
}
还会有一个统一的异常处理类,使用@RestControllerAdvice
标记,然后每个方法会捕捉对应的异常,只要我们使用@ResponseStatus
来标记这些方法,springdoc就会自动生成相应的文档
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(value = Exception.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public Result handleException(HttpServletRequest httpServletRequest, Exception e) {return new Result(StatusCode.FAILED.getCode(), StatusCode.FAILED.getMessage(), null);}@ExceptionHandler(value = ApiException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public Result handleBusinessException(HttpServletRequest httpServletRequest, ApiException e) {return new Result(e.getCode(), e.getMessage(), null);}
}
例如我们有如下代码:
...
@RequestMapping(value = "/api/programmer",produces = "application/json")
public class ProgrammerController {@GetMapping("/{id}")public Result<Programmer> getProgrammer(@Parameter(description = "程序员id") @PathVariable Integer id) {...}
}
生成的api文档如下:
可见,多了400和500。
四、认证授权
我们知道·swagger-ui·不仅可以看API文档也可以直接调用API,这点很牛逼。但有时我们的服务需要认证,否则就调用不通,那怎么办?稍安勿躁,OpenApi规范也考虑到了这个问题。
OpenAPI 3.0 支持下面的认证模式:
- HTTP authentication schemes (使用Authorization header):
- Basic
- Bearer
- Other HTTP schemes as defined by RFC 7235 and HTTP Authentication Scheme Registry
- API keys in headers, query string or cookies
- Cookie authentication
- OAuth 2
- OpenID Connect Discovery
有的我也没用过,我们常用的就是Http Auth,以及OAuth2。本文以HTTP Bearer来作为我们服务的认证授权模式,如下图所示。
3.1无需认证
当你的服务没有认证机制的话是可以直接调用的:
每个API 右上角都有一个try it out按钮,点击输入参数点击execute按钮即可,如下所示。
3.2需要认证
如果你的服务需要认证后才能调用,那么默认情况下就不行了。例如你使用了Spring Security,或者你自己写了个Filter 来实现认证功能。
下面是demo服务用来做认证的Filter,采用HTTP Bearer 模式。所以需要在请求的Authentication Header 里 携带token 123才能通过认证。
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {...@Overrideprotected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {...// get token from header [Authorization: Bearer <token>]String authHeader = request.getHeader(AUTH_HEADER);...String authToken = authHeader.split(" ")[1];if (!"123".equals(authToken)) {genUnauthResponse(response);return;}filterChain.doFilter(request, response);}
所以当从swagger-ui
上调用API时,返回了401,如下图所示。那怎么才能正常调用API呢?接下来让我们看一下
Springdoc 使用@SecurityScheme
来定义一个安全模式,我们可以定义全局的,也可以针对某个controller定义类级别的,我们这里定义一个全局的。
- 在Application类上添加
@SecurityScheme
@SecurityScheme(name = "api_token", type = SecuritySchemeType.HTTP, scheme ="bearer", in = SecuritySchemeIn.HEADER)
@SpringBootApplication
public class SpringdocIntegrateApplication {public static void main(String[] args) {SpringApplication.run(SpringdocIntegrateApplication.class, args);}
}
我们定义了一个名为api_token
的安全模式,并指定了其使用HTTP Bearer的方式。
- 使此安全模式生效
@Configuration
public class SpringDocConfig {@Beanpublic OpenAPI myOpenAPI() {return new OpenAPI()....security(List.of(new SecurityRequirement().addList("api_token")));}
}
注意api_token
正是我们第一步定义的那个Security schema。
经过上面两步后查看生成的API文档,你会发现其右上角出现了一个Authorize
的按钮, 而且每个API的右边也出现了一把小锁,如下图所示。
当点击Authorize
按钮后会弹出一个让你提供认证信息的弹出,其根据不同的认证方式会有所区别。
我们这里需要输入一个token,然后点击Authorize按钮后关闭此弹窗。你会发现每个API右边的那把小锁从打开状态变为了关闭状态,颜色也从灰色变成了黑色,证明其生效了。
然后我们再来请求一下API,就会正常返回了。
- 声明是否需要认证
默认情况下按照上面两步设置后,整个应用程序的API生效,但是有的API是不需要认证的,例如登录。 对于这种情况,我们可以使用@SecurityRequirements()
来设置。
@RestController
@RequestMapping(value = "/admin",produces = "application/json")
public class AuthController {...@SecurityRequirements()@PostMapping("/login")public Result<String> login(@RequestBody LoginRequest request){return Result.ok("123");}
}
@SecurityRequirements()
里面需要一个String数组,里面列出需要使用的@SecurityScheme
,例如我们这里的api_token
。如果不写就说明不需要任何的安全模式,这里就是这种情况。
从上图可以看出,/admin/login
这个API右边没有小锁的标志,表示其不需要认证。针对本案例来说,不需要认证的意思就是:在发起这个请求的时候,不在Authentication header里面附加token。
这里只是展示了HTTP bearer 这种安全模式,其他的原理类似,小朋友们可以开动你们聪明的大脑研究一下,给补充到这里。
五、SpringDoc整合Knife4j
1.前世今生
在更名为Knife4j
之前,原来的名称是叫swagger-bootstrap-ui
,这是两种不一样风格的Ui,对比情况如下:
名称 | 开发语言&框架 | 状态 | 最后版本 | 风格 |
---|---|---|---|---|
Knife4j | Java、JavaScript、Vue | 持续更新中… | 无 | 黑色 |
swagger-bootstrap-ui | Java、JavaScript、jQuery | 停更 | 1.9.6 | 蓝色 |
Knife4j
从开源至今,目前主要经历版本的变化,分别如下:
版本 | 说明 |
---|---|
1.0~1.9.6 | 名称是叫swagger-bootstrap-ui,蓝色风格Ui |
1.9.6 | 蓝色皮肤风格,开始更名,增加更多后端模块 |
2.0~2.0.5 | Ui基于Vue2.0+AntdV重写,黑色风格,参考示例,底层依赖的springfox框架版本是2.9.2,仅提供Swagger2规范的适配 |
2.0.6~2.0.9 | 底层springfox框架版本升级至2.10.5,仅提供Swagger2规范的适配 |
3.0~3.0.3 | 底层依赖springfox框架版本升级至3.0.3,OpenAPI规范是v3,过度版本,建议开发者不要使用 |
4.0~ | 区分OpenAPI2和Swagger3的Maven坐标artifactId OpenAPI2规范服务端解析框架稳定在springfox2.10.5 OpenAPI3框架服务端解析跟随springdoc项目更新迭代 建议开发者使用该版本,请参考4.x升级文档 |
2.Spring Boot版本兼容性
首先,确保您了解您的项目所使用的Spring Boot版本。
以下是一些常见的Spring Boot版本及其对应的Knife4j版本兼容推荐:
Spring Boot版本 | Knife4j Swagger2规范 | Knife4j OpenAPI3规范 |
---|---|---|
1.5.x~2.0.0 | <Knife4j 2.0.0 | >=Knife4j 4.0.0 |
2.0~2.2 | Knife4j 2.0.0 ~ 2.0.6 | >=Knife4j 4.0.0 |
2.2.x~2.4.0 | Knife4j 2.0.6 ~ 2.0.9 | >=Knife4j 4.0.0 |
2.4.0~2.7.x | >=Knife4j 4.0.0 | >=Knife4j 4.0.0 |
>= 3.0 | >=Knife4j 4.0.0 | >=Knife4j 4.0.0 |
Knife4j在之前的版本更新中,逐渐提供了一些服务端适配的增强特性功能。
但是开发者应该明白,不管是Swagger2规范还是OpenAPI3规范,Knife4j的最新版本的纯Ui版本,是可以适配Spring Boot所有版本的。
如果你不考虑使用Knife4j提供的服务端增强功能,引入Knife4j的纯Ui版本没有任何限制。只需要考虑不同的规范即可
3.Spring Boot 3
提示
Spring Boot 3 只支持OpenAPI3规范
Knife4j提供的starter已经引用springdoc-openapi的jar,开发者需注意避免jar包冲突
JDK版本必须 >= 17
详细Demo请参考knife4j-spring-boot3-demo
首先,引用Knife4j的starter,Maven坐标如下:
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>
其实现在就可以使用Knife4j了,暂不做其他配置,启动项目,浏览器输入http://localhost:8080/doc.html
查看接口文档
由于我们没有进行任何的属性配置,所以看到的页面是knife4j的初始页面
引入之后,其余的配置,开发者即可完全参考springdoc-openapi的项目说明,Knife4j只提供了增强部分,如果要启用Knife4j的增强功能,可以在配置文件中进行开启
部分配置如下:
# springdoc-openapi项目配置
springdoc:swagger-ui:path: /swagger-ui.htmltags-sorter: alphaoperations-sorter: alphaapi-docs:path: /v3/api-docsgroup-configs:- group: 'default'paths-to-match: '/**'packages-to-scan: com.xiaominfo.knife4j.demo.web
# knife4j的增强配置,不需要增强可以不配
knife4j:enable: truesetting:language: zh_cn
Knife4j更多增强配置明细,请移步文档进行查看
最后,使用OpenAPI3的规范注解,注释各个Spring的REST接口,示例代码如下:
@RestController
@RequestMapping("body")
@Tag(name = "body参数")
public class BodyController {@Operation(summary = "普通body请求")@PostMapping("/body")public ResponseEntity<FileResp> body(@RequestBody FileResp fileResp){return ResponseEntity.ok(fileResp);}@Operation(summary = "普通body请求+Param+Header+Path")@Parameters({@Parameter(name = "id",description = "文件id",in = ParameterIn.PATH),@Parameter(name = "token",description = "请求token",required = true,in = ParameterIn.HEADER),@Parameter(name = "name",description = "文件名称",required = true,in=ParameterIn.QUERY)})@PostMapping("/bodyParamHeaderPath/{id}")public ResponseEntity<FileResp> bodyParamHeaderPath(@PathVariable("id") String id,@RequestHeader("token") String token, @RequestParam("name")String name,@RequestBody FileResp fileResp){fileResp.setName(fileResp.getName()+",receiveName:"+name+",token:"+token+",pathID:"+id);return ResponseEntity.ok(fileResp);}
}
最后,访问Knife4j的文档地址:http://ip:port/doc.html
即可查看文档
4.Spring Boot 2
提示
Spring Boot 版本建议 2.4.0~3.0.0之间
Spring Boot 版本 < 2.4 版本则建议选择Knife4j 4.0之前的版本
Spring Boot 2 + OpenAPI2 demo:knife4j-spring-boot27-demo
Spring Boot 2 + OpenAPI3 demo:knife4j-springdoc-openapi-demo
OpenAPI2
OpenAPI2(Swagger)规范是Knife4j之前一直提供支持的版本,底层依赖框架为Springfox,此次在4.0版本开始
Knife4j有了新的变化,主要有以下几点:
- Springfox版本选择的依然是2.10.5版本,而并非springfox最新3.0.0版本
- 不支持以Springfox框架为基础的OpenAPI3规范,放弃Springfox项目的后续版本适配支持工作
- Spring Boot 版本建议 2.4.0~3.0.0之间
可以使用 knife4j-openapi2-spring-boot-starter,maven 坐标如下:
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>
配置yml属性,如下:
knife4j:enable: trueopenapi:title: Knife4j官方文档description: "`我是测试`,**你知道吗**# aaa"email: xiaoymin@foxmail.comconcat: 八一菜刀url: https://docs.xiaominfo.comversion: v4.0license: Apache 2.0license-url: https://stackoverflow.com/terms-of-service-url: https://stackoverflow.com/group:test1:group-name: 分组名称api-rule: packageapi-rule-resources:- com.knife4j.demo.new3
最后,访问Knife4j的文档地址:http://ip:port/doc.html
即可查看文档
OpenAPI3
OpenAPI3的规范,目前针对Java的Spring Boot项目,主要支持的有2个版本
- springfox 3.0.0: 同时兼容OpenAPI2以及OpenAPI3,但是停更很久了
- springdoc-openapi: 兼容OpenAPI3规范,更新速度频繁
Knife4j在只有的OpenAPI3规范中,底层基础框架选择springdoc-openapi项目
针对Springfox3.0.0版本会放弃。
建议开发者如果使用OpenAPI3规范的话,也尽快迁移过来。
可以使用 knife4j-openapi3-spring-boot-starter,maven 坐标如下:
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</artifactId><version>4.5.0</version>
</dependency>
引入jar包后,同上面的Spring Boot 3版本使用方式一样,进行配置即可。