Опубликовать обработанную страницу Freemarker в Ktor - PullRequest
0 голосов
/ 22 октября 2019

Я использую шаблоны Freemarker в рамках веб-сервера Ktor. По умолчанию я передаю FreeMarkerContent в ответ:

        get("/login") {
            val resp = FreeMarkerContent("login.ftl", NO_PARAM, "e")
            call.respond(resp)
        }

Я хотел бы добавить специальный пост-процесс, примененный к отображаемой веб-странице, поэтому мой конвейер процесса должен быть: template + data -- [Freemarker] -> отображаемая страница - [Постпроцессор] -> конечная страница. Как я могу вставить фазу постобработки в?

Мои идеи:

  1. Рендеринг страницы вручную, затем применить процессор также вручную. Для этого мне нужен экземпляр Freemarker для вызова исделать вывод, который должен быть основан на тексте, чтобы можно было получить и опубликовать процесс. В настоящее время я не могу найти, как получить этот экземпляр и как создать обработанную страницу.
  2. Внедрение какого-либо процессора в конвейер процесса. Мне нужен какой-то способ добавить процессор в конвейер, но он только проходитСодержимое, которое выполняется вызовом.

РЕДАКТИРОВАТЬ:

Я обнаружил, что могу создать новую функцию, и на примере я это сделал. После нескольких часов попыток, сброса кода и нескольких неудачных попыток я смог перехватить вывод Freemarker, но ничего не смог с ним сделать, чтобы получить отрендеренный текст.

Наконец, увидев, что адаптер Freemarker тонкий, я принял ужасное (надеюсь временное) решение: я клонировал исходный код функции Freemarker и создал свою собственную версию, внедрив свой код постобработки:

/**
 * Represents a content handled by [GlossaryFreeMarker] feature.
 *
 * @param template name that is resolved by freemarker
 * @param model to be passed during template rendering
 * @param etag value for `E-Tag` header (optional)
 * @param contentType of response (optional, `text/html` with UTF-8 character encoding by default)
 */
class GlossaryFreeMarkerContent (
        val template: String,
        val model: Any?,
        val etag: String? = null,
        val contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8)
)

/**
 * Freemarker support feature. Provides ability to respond with [FreeMarkerContent]
 */
class GlossaryFreeMarker(private val config: Configuration) {
    /**
     * A feature installing companion object
     */
    companion object Feature : ApplicationFeature<ApplicationCallPipeline, Configuration, GlossaryFreeMarker> {
        override val key: AttributeKey<GlossaryFreeMarker> = AttributeKey("glossaryFreemarker")

        override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): GlossaryFreeMarker {
            val config = Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).apply(configure)
            val feature = GlossaryFreeMarker(config)
            pipeline.sendPipeline.intercept(ApplicationSendPipeline.Transform) { value ->
                if (value is GlossaryFreeMarkerContent) {
                    val response = feature.process(value)
                    proceedWith(response)
                }
            }
            return feature
        }
    }

    private fun process(content: GlossaryFreeMarkerContent): FreeMarkerOutgoingContent {
        return FreeMarkerOutgoingContent(
                config.getTemplate(content.template),
                content.model,
                content.etag,
                content.contentType
        )
    }

    private class FreeMarkerOutgoingContent(
            val template: Template,
            val model: Any?,
            etag: String?,
            override val contentType: ContentType
    ) : OutgoingContent.WriteChannelContent() {
        override suspend fun writeTo(channel: ByteWriteChannel) {
            channel.bufferedWriter(contentType.charset() ?: Charsets.UTF_8).use {
                // This is where I "injected my code"
                val w = StringWriter()
                template.process(model, w)
                // Do my transformation
                val res = MyTransformation.transform(w.toString())
                it.write(res)
            }
        }

        init {
            if (etag != null)
                versions += EntityTagVersion(etag)
        }
    }
}

Это решение работает. Это решение имеет ограничения (все мои страницы должны быть шаблоны Freemarker) Это решение ужасно. Это решение, безусловно, не правильный способ сделать это ...

...