如何使用 http4k 公开 swagger UI?

How to expose swagger UI with http4k?

我正在使用 http4k framework using their Contract APIs 构建微服务。我可以很容易地在 eg. 上暴露大摇大摆的 API 描述 JSON。 /swagger.json

fun app(): HttpHandler = "/" bind contract {
    renderer = OpenApi3(ApiInfo("GoOut Locations API", "1.0"), Jackson)
    descriptionPath = "/swagger.json"
    routes += ...
}

有没有一种简单的方法来公开 swagger UI 以便 1) 我可以指定它将可用的路径。 (例如 /swagger-ui) 2) UI 将被预先配置为从上面指定的 descriptionPath 中获取描述 JSON。

理想的 API 应该是这样的

fun app(): HttpHandler = "/" bind contract {
    renderer = OpenApi3(ApiInfo("GoOut Locations API", "1.0"), Jackson)
    descriptionPath = "/swagger.json"
    uiPath = "/swagger-ui"
    routes += ...
}

http4k 未随 OpenApi UI 版本一起提供。您可以通过以下方式轻松发布 UI 版本:

  1. 解压 OpenApi UI 到 src/main/resources/public 文件夹
  2. 使用 static 路由块来为资源提供服务。这里有一个例子:https://github.com/http4k/http4k-by-example/blob/22dcc9a83c497253c29830d5bc981afa5fbbe4ff/src/main/kotlin/verysecuresystems/SecuritySystem.kt#L61

经过一番搜索后,我结合使用 Web Jars 和 http4k 的静态路由实现了这一点。

文档的潜在查看者必须简单地访问 /docs 他被重定向到 /docs/index.html?url=<path to Api description> 的路径

  • index.html 是一个静态的 Swagger UI 入口点,由 Web jar 提供。
  • url 查询参数告诉 swagger UI 从哪里获取 OpenApi 描述。

从 DX 的角度来看,我们有一个简单的 http4k 应用程序:

// path the OpenApi description will be exposed on
private const val API_DESCRIPTION_PATH = "/swagger.json"

fun app(): HttpHandler {
    val api = contract {
        renderer = OpenApi3(ApiInfo("Your API summary", "1.0"), Jackson)
        descriptionPath = API_DESCRIPTION_PATH
        // the actual API routes
        routes += ... 
    }

     return routes(
         // the docs routes are not considered part of the API so we define them outside of the contract
         swaggerUi(API_DESCRIPTION_PATH),
         api
     )
}

swaggerUi 处理程序实现如下

/**
 * Exposes Swagger UI with /docs path as its entry point.
 * @param descriptionPath absolute path to API description JSON. The UI will be configured to fetch it after load.
 */
fun swaggerUi(descriptionPath: String): RoutingHttpHandler = routes(
    "docs" bind Method.GET to {
        Response(Status.FOUND).header("Location", "/docs/index.html?url=$descriptionPath")
    },
    // For some reason the static handler does not work without "/" path prefix.
    "/docs" bind static(Classpath("META-INF/resources/webjars/swagger-ui/3.25.2"))
)

我们还必须包括 swagger-ui webjar 作为我们的依赖项。这是一个 Gradle 指令:

implementation 'org.webjars:swagger-ui:3.25.2'

有关 Maven(及更多)指令,请参阅 webjars 网站。

请注意,swaggerUi 处理程序假定它绑定到整个服务的 / 根路径。但是,这很容易解决。

使用 webjar 的解决方案不再适用于 SwaggerUI 版本 >= 4.1.3,因为 url 参数被忽略(参见 this issue / the release notes)。 URL 必须在 HTML 中指定,或者需要在 HTML 中启用 url 参数。 所以现在的解决方案似乎是解压 UI,更新 index.html,然后直接提供服务,而不是通过 webjar。