Как раскрыть пользовательский интерфейс чванства с помощью http4k?

Я создаю микросервис с фреймворком http4k, используя их API контрактов. Я могу легко раскрыть описание API чванства в формате JSON, например./swagger.json с

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

Есть ли простой способ раскрыть пользовательский интерфейс swagger, чтобы 1) я мог указать путь, по которому он будет доступен. (например./swagger-ui) 2) Пользовательский интерфейс будет предварительно настроен для получения описания JSON из descriptionPath указанное выше.

Идеальный API будет выглядеть примерно так

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

4 ответа

Решение

После небольшого поиска я добился этого с помощью комбинации Web Jars и статической маршрутизации http4k.

Потенциальный зритель документов должен просто посетить /docs путь, по которому его перенаправляют /docs/index.html?url=<path to Api description> где

  • index.html - статическая точка входа пользовательского интерфейса Swagger, обслуживаемая из веб-файла.
  • url Параметр запроса сообщает пользовательскому интерфейсу Swagger, откуда брать описание 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"))
)

Мы также должны включить webjar swagger-ui в качестве нашей зависимости. Вот директива Gradle:

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

См. Веб-сайт webjars для получения информации о директивах Maven (и других).

Обратите внимание, что swaggerUi обработчик предполагает, что он привязан к /корневой путь всего сервиса. Однако это легко исправить.

Решение с использованием webjar больше не работает для версии SwaggerUI >= 4.1.3, так как параметр игнорируется (см. эту проблему / примечания к выпуску ). URL-адрес должен быть указан либо в HTML, либо в urlпараметр должен быть включен в HTML. Так что на данный момент решение, похоже, состоит в том, чтобы распаковать пользовательский интерфейс, обновить index.htmlи обслуживать напрямую, а не через webjar.

Начиная с http4k 4.28.1.0, теперь есть способ сделать это. См. следующий код, взятый с этой страницы документации :

      package guide.howto.create_a_swagger_ui

import org.http4k.contract.contract
import org.http4k.contract.meta
import org.http4k.contract.openapi.ApiInfo
import org.http4k.contract.openapi.v3.OpenApi3
import org.http4k.contract.ui.swaggerUi
import org.http4k.core.Body
import org.http4k.core.ContentType
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK
import org.http4k.core.Uri
import org.http4k.core.with
import org.http4k.lens.string
import org.http4k.routing.routes
import org.http4k.server.SunHttp
import org.http4k.server.asServer

fun main() {
    val greetingLens = Body.string(ContentType.TEXT_PLAIN).toLens()

    // Define a single http route for our contract
    val helloHandler = "/v1/hello" meta {
        operationId = "v1Hello"
        summary = "Say Hello"
        returning(OK, greetingLens to "Sample Greeting")
    } bindContract GET to { _: Request ->
        Response(OK).with(greetingLens of "HI!")
    }

    // Define a contract, and render an OpenApi 3 spec at "/spec"
    val v1Api = contract {
        routes += helloHandler
        renderer = OpenApi3(
            ApiInfo("Hello Server - Developer UI", "99.3.4")
        )
        descriptionPath = "spec"
    }

    // Build a Swagger UI based on the OpenApi spec defined at "/spec"
    val ui = swaggerUi(
        Uri.of("spec"),
        title = "Hello Server",
        displayOperationId = true
    )

    // Combine our api, spec, and ui; then start a server
    // The Swagger UI is available on the root "/" path
    routes(v1Api, ui)
        .asServer(SunHttp(8080))
        .start()
        .block()
}

Http 4k не поставляется с версией пользовательского интерфейса OpenApi. Вы можете легко отправить версию пользовательского интерфейса:

  1. распаковка пользовательского интерфейса OpenApi в папку src / main / resources / public
  2. Используя staticблокировка маршрутизации на сервер ресурсов. Вот пример этого: https://github.com/http4k/http4k-by-example/blob/22dcc9a83c497253c29830d5bc981afa5fbbe4ff/src/main/kotlin/verysecuresystems/SecuritySystem.kt
Другие вопросы по тегам