Я использую шаблоны Freemarker в рамках веб-сервера Ktor. По умолчанию я передаю FreeMarkerContent в ответ:
get("/login") {
val resp = FreeMarkerContent("login.ftl", NO_PARAM, "e")
call.respond(resp)
}
Я хотел бы добавить специальный пост-процесс, примененный к отображаемой веб-странице, поэтому мой конвейер процесса должен быть: template + data -- [Freemarker] -> отображаемая страница - [Постпроцессор] -> конечная страница. Как я могу вставить фазу постобработки в?
Мои идеи:
- Рендеринг страницы вручную, затем применить процессор также вручную. Для этого мне нужен экземпляр Freemarker для вызова исделать вывод, который должен быть основан на тексте, чтобы можно было получить и опубликовать процесс. В настоящее время я не могу найти, как получить этот экземпляр и как создать обработанную страницу.
- Внедрение какого-либо процессора в конвейер процесса. Мне нужен какой-то способ добавить процессор в конвейер, но он только проходитСодержимое, которое выполняется вызовом.
РЕДАКТИРОВАТЬ:
Я обнаружил, что могу создать новую функцию, и на примере я это сделал. После нескольких часов попыток, сброса кода и нескольких неудачных попыток я смог перехватить вывод 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) Это решение ужасно. Это решение, безусловно, не правильный способ сделать это ...