Spring 5 中一個非常重要的更新就是增加了響應式web開發WebFlux,並且推薦使用函式式風格(RouterFunction
和 HandlerFunction
)來開發WebFlux。對於之前主流的MVC開發模式,Spring也順道給它提供了和WebFlux函式式開發幾乎一致的方式(見上文《Spring 5 MVC 中的 Router Function 使用》)。這樣,響應式WebFlux和非響應式MVC都可以通過Controller
來實現,也都可以通過RouterFunction
來實現。
但是Spring推薦使用函式式方式;而且聽說在所有場景也推薦使用WebFlux,後續有可能廢棄掉非響應式MVC
對於通過Controller
來實現的介面,swagger可以直接掃描處理。使用了RouterFunction
的怎麼辦呢?這篇文章我們來看一下如果通過springdoc這個專案來實現函式式介面的文件生成。
pom依賴
假設你使用的是maven。如果使用gradle我估計也差不多
要使用springdoc-openapi,需要新增它的依賴。一般需要兩個,第一個是基礎公共依賴:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.9</version>
</dependency>
另一個根據物件不同需要引入的也不同。我們這裡先處理非響應式MVC的介面,需要依賴
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.5.9</version>
</dependency>
依賴的版本號可以自己去查一下最新的
RouterOperation
接下來需要去RouterFunction上面定義swagger的顯示資訊,在controller
中使用的註解是io.swagger.v3.oas.annotations.Operation
(如果是版本2使用的註解是io.swagger.annotations.ApiOperation
),現在需要使用org.springdoc.core.annotations.RouterOperation
。以《Spring 5 MVC 中的 Router Function 使用》中的介面為例,需要這樣寫
@Configuration
public class RoutingConfig {
@Bean
@RouterOperations({
@RouterOperation(
path = "/model/building/{entId}/stations",
beanClass = ModelBuildingHandler.class,
beanMethod = "getStations",
method = RequestMethod.GET,
operation = @Operation(
operationId = "getStations",
parameters = @Parameter(
name = "entId",
in = ParameterIn.PATH,
required = true,
description = "企業ID"
)
)
)
})
public RouterFunction<ServerResponse> getModelBuildingRouters(ModelBuildingHandler modelBuildingHandler) {
return RouterFunctions.nest(path("/model/building"),
RouterFunctions.route(GET("/{entId}/stations"), modelBuildingHandler::getStations)
.andRoute(GET("/{stationId}/device-types"), modelBuildingHandler::getDeviceTypes)
);
}
}
不同於Controller
中的介面會被自動發現,這裡雖然有兩個方法但是我們只定義了一個RouterOperation
操作,會導致看不到第二個方法。
我們修改一下application.properties就能看到效果了:
springdoc.packagesToScan=要掃描的包
springdoc.pathsToMatch=/**
注意springdoc.packagesToScan這裡,需要寫一個包含了
RouterFunction
和HandlerFunction
實現的包。我當時只寫了RouterFunction
所在的包,一直不成功
再響應增加一個RouterOperation
即可:
@RouterOperations({
@RouterOperation(
path = "/model/building/{entId}/stations",
beanClass = ModelBuildingHandler.class,
beanMethod = "getStations",
method = RequestMethod.GET,
operation = @Operation(
operationId = "getStations",
parameters = @Parameter(
name = "entId",
in = ParameterIn.PATH,
required = true,
description = "企業ID"
)
)
),
@RouterOperation( // 這裡我省略了一些屬性配置
path = "/model/building/{stationId}/device-types",
beanClass = ModelBuildingHandler.class,
beanMethod = "getDeviceTypes"
)
})
RequestBody
上面講的都是GET的請求,如果需要設定請求體格式怎麼辦呢?
在RouterFunction中增加一個POST請求(下面最後一個url):
public RouterFunction<ServerResponse> getModelBuildingRouters(ModelBuildingHandler modelBuildingHandler) {
return RouterFunctions.nest(
path("/model/building"),
RouterFunctions.route(GET("/{entId}/stations"), modelBuildingHandler::getStations)
.andRoute(GET("/{stationId}/device-types"), modelBuildingHandler::getDeviceTypes)
.andRoute(POST("/devices/points/real-time-data"), modelBuildingHandler::getRealTimeData)
);
}
它對應的RouterOperation
如下:
@RouterOperation(
path = "/model/building/devices/points/real-time-data",
beanClass = ModelBuildingHandler.class,
beanMethod = "getRealTimeData",
operation = @Operation(
operationId = "getRealTimeData",
requestBody = @RequestBody(
required = true,
description = "請求體",
content = @Content(
schema = @Schema(implementation = RealTimeDataQueryVO.class)
)
)
)
)
要在Handler中拿到請求引數,使用body方法:RealTimeDataQueryVO queryVo = req.body(RealTimeDataQueryVO.class);
public ServerResponse getRealTimeData(ServerRequest req) {
RealTimeDataQueryVO queryVo;
try {
queryVo = req.body(RealTimeDataQueryVO.class);
log.info("請求引數{}", queryVo);
} catch (Exception e) {
throw new RuntimeException(e);
}
RealTimeDataResultBO resultBo = modelBuildingService.getRealTimeData(transferRealTimeDataQueryParam(queryVo));
return body(RdfaResult.success(transferRealTimeDataResult(resultBo)));
}
上面就是如何在函式式MVC程式設計中使用swagger。可以看到比較繁瑣,3行程式碼對應了差不多30行swagger配置。
從swagger v2 遷移
如果之前的專案使用的是springfox之類的swagger版本2,需要將其依賴移除,並修改類上的註解。
主要的變更點如下圖:
Swagger配置
這一步是可選的,幾乎沒什麼必要。
可以像之前一樣定義文件的歸屬、協議、版本等等:
@Configuration
public class DocConfig {
@Bean
public OpenAPI springShopOpenApi() {
return new OpenAPI()
.info(new Info().title("測試 API")
.description("樣例程式")
.version("v0.0.1")
.license(new License()
.name("Apache 2.0我的協議")
.url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("外部文件連結")
.url("https://springshop.wiki.github.org/docs"));
}
}
WebFlux 中的文件
如果專案不是mvc的而是webFlux的,相應的Maven依賴改成下面的即可:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.5.12</version>
</dependency>
其他類似。