DSLを使ってHTMLを配信

Estimated reading time: 1 minute

この機能はkotlinx.htmlと統合されており、 HTMLをChunked transfer encodingを使って直接配信しているため、HTML全体のためにメモリ空間を保持する必要がありません。

This feature is defined in the class io.ktor.html.HtmlContent in the artifact io.ktor:ktor-html-builder:$ktor_version.
dependencies { implementation "io.ktor:ktor-html-builder:$ktor_version" }
dependencies { implementation("io.ktor:ktor-html-builder:$ktor_version") }
<project> ... <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-html-builder</artifactId> <version>${ktor.version}</version> <scope>compile</scope> </dependency> </dependencies> </project>

インストール

この機能はインストールを必要としません。

基本的な使い方

レスポンスの生成時に、respond/respondTextメソッドを呼び出す代わりに、ApplicationCall.respondHtmlを呼び出します:

call.respondHtml {
    head {
        title { +"Async World" }
    }
    body {
        h1(id = "title") {
            +"Title"
        }
    }
}

kotlinx.htmlを使ってHTMLを生成する方法に関するドキュメントとしては、wikiを参照ください。

テンプレートとレイアウト

DSLによるプレーンなHTMLの生成に加え、Ktorでは型付けされたシンプルなテンプレートエンジンが利用できます。 それを使うことで複雑なレイアウトを型安全な方法で生成できます。 とてもシンプルですが、パワフルです:

call.respondHtmlTemplate(MulticolumnTemplate()) {
    column1 {
        +"Hello, $name"
    }
    column2 {
        +"col2"
    }
}

class MulticolumnTemplate(val main: MainTemplate = MainTemplate()) : Template<HTML> {
    val column1 = Placeholder<FlowContent>()
    val column2 = Placeholder<FlowContent>()
    override fun HTML.apply() {
        insert(main) {
            menu {
                item { +"One" }
                item { +"Two" }
            }
            content {
                div("column") {
                    insert(column1)
                }
                div("column") {
                    insert(column2)
                }
            }
        }
    }
}

class MainTemplate : Template<HTML> {
    val content = Placeholder<HtmlBlockTag>()
    val menu = TemplatePlaceholder<MenuTemplate>()
    override fun HTML.apply() {
        head {
            title { +"Template" }
        }
        body {
            h1 {
                insert(content)
            }
            insert(MenuTemplate(), menu)
        }
    }
}

class MenuTemplate : Template<FlowContent> {
    val item = PlaceholderList<UL, FlowContent>()
    override fun FlowContent.apply() {
        if (!item.isEmpty()) {
            ul {
                each(item) {
                    li {
                        if (it.first) b {
                            insert(it)
                        } else {
                            insert(it)
                        }
                    }
                }
            }
        }
    }
}

この例のように、Template<TFlowContent>を実装したクラスを定義し、TFlowContent.applyをオーバーライドし、 PlaceholderTemplatePlaceholderプロパティを任意で定義する必要があります。

call.respondHtmlTemplate(MulticolumnTemplate()) { }でテンプレートを生成するとき、 テンプレートをレシーバとして取得でき、 プロパティとして定義されたプレースホルダーに型安全な方法でアクセスすることができます。