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版本使用方式一樣,進行配置即可。