一、前言
OpenAPI 簡介
OpenAPI規范是一個由社區驅動的開放規范,這個規范定義了一個標准的、語言無關的 RESTful API 接口規范,它可以同時允許開發人員和操作系統查看並理解某個服務的功能,而無需訪問源代碼,文檔或網絡流量檢查。合理地定義OpenAPI規范,能使開發者更好的理解遠程服務和與之交互。目前最新的規范是OpenAPI Specification 3.0.3。
Swagger簡介
Swagger是一個規范和完整的框架,用於生成 、描述、調用和可視化 RESTful 風格的 Web 服務,簡化了開發者開發API。
Springfox簡介
SpringFox是 Spring 社區非官方維護的一個項目,主要就是幫助使用者將Swagger集成到Spring 中,在停更了2年多之后,近期終於推出了3.0.0版本,支持了開發者期盼已久的OpenApi 3規范。
二、Springfox 3.0.0 在Spring Boot 項目下的用法
導入依賴(Gradle)
implementation('io.springfox:springfox-boot-starter:3.0.0')
開啟注解
@EnableOpenApi
編寫配置類
/** * @author Vieruodis * @description Springfox openApi 3.0 的配置類 * @date 2020/7/15 21:43 */ @Configuration @EnableOpenApi public class SpringfoxOpenApiConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.OAS_30) .pathMapping("/") // 定義是否開啟swagger,false為關閉,可以通過變量控制 .enable(true) // 將api的元信息設置為包含在json ResourceListing響應中。 .apiInfo(apiInfo()) // 選擇哪些接口作為swagger的doc發布 .select() .apis(RequestHandlerSelectors.basePackage("com.vieruodis.vmall.manage.product.controller")) .paths(PathSelectors.any()) .build() .groupName("defaultBB") // 支持的通訊協議集合 .protocols(Set.of("https", "http")) ; } /** * API 頁面上半部分展示信息 */ private ApiInfo apiInfo() { return new ApiInfoBuilder() .version("1.0.0") .title("Vieruodis Api Doc") .description("Vmall shop application") .contact(new Contact("Vieruodis", null, "huaiyuan0660@outlook.com")) .termsOfServiceUrl("http://localhost:88/") .license("Apache 2.0") .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0.html") .build(); } }
編寫Controller的注解
@Api(value = "AlbumController") public interface AlbumControllerApi { /** * Album 多條件分頁查詢 * * @param page 頁碼 * @param size 每頁數 * @param album 查詢條件 * @return 封裝了 【Album 分頁數據】 的響應結果 */ @ApiOperation(value = "Album條件分頁查詢",notes = "分頁條件查詢Album方法詳情",tags = {"AlbumController"}) @ApiImplicitParams({ @ApiImplicitParam(paramType = "path", name = "page", value = "當前頁", required = true, dataType = "Integer"), @ApiImplicitParam(paramType = "path", name = "size", value = "每頁顯示條數", required = true, dataType = "Integer") }) PageResponseResult<Album> findPage(int page, int size, @ApiParam(name = "Album對象",value = "傳入JSON數據") Album album); /** * Album 分頁查詢 * * @param page 頁碼 * @param size 每頁數 * @return 封裝了 【Album 分頁數據】 的響應結果 */ @ApiOperation(value = "Album分頁查詢",notes = "分頁查詢Album方法詳情",tags = {"AlbumController"}) @ApiImplicitParams({ @ApiImplicitParam(paramType = "path", name = "page", value = "當前頁", required = true, dataType = "Integer"), @ApiImplicitParam(paramType = "path", name = "size", value = "每頁顯示條數", required = true, dataType = "Integer") }) PageResponseResult<Album> findPage(int page, int size); /** * Album 多條件查詢 * * @param album 查詢條件 * @return 封裝了 【Album 數據集】 的響應結果 */ @ApiOperation(value = "Album條件查詢",notes = "條件查詢Album方法詳情",tags = {"AlbumController"}) CommonResponseResult<List<Album>> findList(@ApiParam(name = "Album對象",value = "傳入JSON數據") Album album); /** * 查詢所有 Album * * @return 封裝了 【Album 數據集】 的響應結果 */ @ApiOperation(value = "查詢所有Album",notes = "查詢所Album有方法詳情",tags = {"AlbumController"}) CommonResponseResult<List<Album>> findAll(); /** * 根據ID查詢 Album * * @param id 實體ID * @return 封裝了 【Album 數據】 的響應結果 */ @ApiOperation(value = "Album根據ID查詢",notes = "根據ID查詢Album方法詳情",tags = {"AlbumController"}) @ApiImplicitParam(paramType = "path", name = "id", value = "主鍵ID", required = true, dataType = "Long") CommonResponseResult<Album> findById(Long id); /** * 新增 Album * * @param album 新增實體 * @return 通用響應結果 */ @ApiOperation(value = "Album添加",notes = "添加Album方法詳情",tags = {"AlbumController"}) ResponseResult add(@ApiParam(name = "Album對象",value = "傳入JSON數據",required = true) Album album); /** * 修改 Album * * @param id 實體ID * @param album Album 數據 * @return 通用響應結果 */ @ApiOperation(value = "Album根據ID修改",notes = "根據ID修改Album方法詳情",tags = {"AlbumController"}) @ApiImplicitParam(paramType = "path", name = "id", value = "主鍵ID", required = true, dataType = "Long") ResponseResult update(Long id, @ApiParam(name = "Album對象",value = "傳入JSON數據") Album album); /** * 刪除 Album * * @param id 實體ID * @return 通用響應結果 */ @ApiOperation(value = "Album根據ID刪除",notes = "根據ID刪除Album方法詳情",tags = {"AlbumController"}) @ApiImplicitParam(paramType = "path", name = "id", value = "主鍵ID", required = true, dataType = "Long") ResponseResult delete(Long id); }
編寫實體的注解
@Data @ApiModel(description = "Album",value = "Album") @Entity @Table(name="tb_album") public class Album implements Serializable { /** * 編號 */ @ApiModelProperty(value = "編號") @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; /** * 相冊名稱 */ @ApiModelProperty(value = "相冊名稱") @Column(name = "title") private String title; /** * 相冊封面 */ @ApiModelProperty(value = "相冊封面") @Column(name = "image") private String image; /** * 圖片列表 */ @ApiModelProperty(value = "圖片列表") @Column(name = "image_items") private String imageItems; }
測試
打開: http://localhost:8000/swagger-ui/index.html
三、與Spring Cloud Gateway集成
整合思路
1,定義一個SwaggerProvider類用於填充右上角下拉框的數據,格式是(name,oas3Url)
2,定義過濾器,對oas3Url進行修正,讓其能夠獲取文檔
實踐
導入依賴(Gradle):
implementation('io.springfox:springfox-boot-starter:3.0.0')
定義SwaggerProvider類填充右上角下拉框數據
@ConfigurationProperties(prefix = "open-api.swagger") @EnableConfigurationProperties @Component @Primary public class SwaggerProvider implements SwaggerResourcesProvider { @Setter @Getter private List<String> resources; public static final String API_URI = "/v3/api-docs"; @Override public List<SwaggerResource> get() { List<SwaggerResource> swaggerResources = new ArrayList<>(); resources.forEach(s -> { //name,oas3Url,group String[] split = s.split(","); swaggerResources.add(resource(split[0], split[1] + API_URI, split[2])); }); return swaggerResources; } /** * @param name 顯示的名字 * @param oas3Url 文檔格式:/manage/product/v3/api-docs(?group=...) */ private SwaggerResource resource( String name, String oas3Url, String swaggerGroup) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); //url: 要看其他微服務是否設置了group 設置了的話 if (Docket.DEFAULT_GROUP_NAME.equals(swaggerGroup)) { swaggerResource.setUrl(oas3Url); } else { //其他微服務是否設置了group 需要加上"?group=" + 組名 swaggerResource.setUrl(oas3Url + "?group=" + swaggerGroup); } System.out.println("name: " + name + " url: " + oas3Url); swaggerResource.setSwaggerVersion("3.0.3"); return swaggerResource; } }
填充的數據來源於配置文件:
open-api:
swagger:
# 僅供 springfox 使用
resources: # name,oas3Url,group
- manage-product-service-1,/manage/product,defaultBB
- manage-product-service-2,/manage/product,manage-product
- file-service,/file,default
上面我給它填充的url是:localhost:端口號/【manage/product】/v3/api-docs(?group= ) 形式的,目的是為了讓它找到微服務,因為我的路由設置是:
spring:
cloud:
gateway:
routes:
- id: vmall-manage-product-service
uri: lb://vmall-manage-product
predicates:
- Path=/manage/product/**
但是Springfox獲取文檔的地址格式是:localhost:端口號/v3/api-docs(?group= ),所以我們需要定義一個過濾器,讓它能夠獲取到文檔:
@Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); System.out.println("path:"+path); if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) { return chain.filter(exchange); } ServerHttpRequest newRequest = request.mutate().path(SwaggerProvider.API_URI).build(); ServerWebExchange newWebExchange = exchange.mutate().request(newRequest).build(); return chain.filter(newWebExchange); }; } }
給路由配上過濾器:
spring:
cloud:
gateway:
routes:
- id: vmall-manage-product-service
uri: lb://vmall-manage-product
predicates:
- Path=/manage/product/**
filters:
- SwaggerHeaderFilter
測試:
打開:http://localhost:88/swagger-ui/index.html
測試調用文檔的方法,請求的是 localhost:88/manage/product/album , 這樣通過網關就能找到對應的微服務,並且正確返回json數據了。
四、總結
Springfox3.0.0中支持了webflux,所以就不需要再寫swaggerHandler解決請求映射了,相比SpringDoc openApi整合Spring Cloud Gateway 比較簡單,個人比較推薦用來整合Spring Cloud Gateway。