レスポンスの送信
Ktorでは、ルートハンドラー内で受信したリクエストを処理し、レスポンスを送信することができます。プレーンテキスト、HTMLドキュメントやテンプレート、シリアライズされたデータオブジェクトなど、さまざまなタイプのレスポンスを送信できます。また、コンテンツタイプ、ヘッダー、クッキー、ステータスコードなどのさまざまなレスポンスパラメータを構成することも可能です。
ルートハンドラー内では、レスポンスを操作するために以下のAPIを利用できます。
call.respondText()やcall.respondHtml()など、特定のコンテンツタイプを送信するための関数群。- レスポンス内で任意のデータタイプを送信できる
call.respond()関数。ContentNegotiation プラグインがインストールされている場合、特定の形式でシリアライズされたデータオブジェクトを送信できます。 ApplicationResponseオブジェクトを返すcall.response()プロパティ。ステータスコードの設定、ヘッダーの追加、クッキーの構成など、レスポンスパラメータへのアクセスを提供します。- リダイレクトレスポンスを送信するための
call.respondRedirect()関数。
レスポンスペイロードの設定
プレーンテキスト
プレーンテキストを送信するには、call.respondText() 関数を使用します。
get("/") {
call.respondText("Hello, world!")
}HTML
Ktorは、HTMLレスポンスを生成するための主に2つのメカニズムを提供しています。
- Kotlin HTML DSLを使用したHTMLの構築。
- FreeMarker や Velocity などのJVMテンプレートエンジンを使用したテンプレートのレンダリング。
完全なHTMLドキュメント
Kotlin DSLで構築された完全なHTMLドキュメントを送信するには、call.respondHtml() 関数を使用します。
get("/") {
val name = "Ktor"
call.respondHtml(HttpStatusCode.OK) {
head {
title {
+name
}
}
body {
h1 {
+"Hello from $name!"
}
}
}
}HTMLフラグメント
<html>、<head>、<body> で囲わずに、HTMLの断片のみを返す必要がある場合は、call.respondHtmlFragment() を使用できます。
get("/fragment") {
call.respondHtmlFragment(HttpStatusCode.Created) {
div("fragment") {
span { +"Created!" }
}
}
}
}テンプレート
レスポンスでテンプレートを送信するには、特定のコンテンツを指定して call.respond() 関数を使用します。
get("/index") {
val sampleUser = User(1, "John")
call.respond(FreeMarkerContent("index.ftl", mapOf("user" to sampleUser)))
}また、call.respondTemplate() 関数を使用することもできます。
get("/index") {
val sampleUser = User(1, "John")
call.respondTemplate("index.ftl", mapOf("user" to sampleUser))
}詳細は テンプレート のヘルプセクションを参照してください。
オブジェクト
Ktorでデータオブジェクトのシリアライズを有効にするには、ContentNegotiation プラグインをインストールし、必要なコンバーター(例:JSON)を登録する必要があります。その後、call.respond() 関数を使用してレスポンスにデータオブジェクトを渡すことができます。
routing {
get("/customer/{id}") {
val id: Int by call.parameters
val customer: Customer = customerStorage.find { it.id == id }!!
call.respond(customer)完全な例については、json-kotlinx を参照してください。
ファイル
クライアントにファイルのコンテンツで応答するには、2つのオプションがあります。
Fileオブジェクトとして表されるファイルの場合は、call.respondFile()関数を使用します。- 指定された
Pathオブジェクトによって指されるファイルの場合は、LocalPathContentクラスを指定してcall.respond()関数を使用します。
以下の例は、ファイルを送信し、Content-Disposition ヘッダーを追加してダウンロード可能にする方法を示しています。
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.server.plugins.autohead.*
import io.ktor.server.plugins.partialcontent.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.io.File
import java.nio.file.Path
fun Application.main() {
install(PartialContent)
install(AutoHeadResponse)
routing {
get("/download") {
val file = File("files/ktor_logo.png")
call.response.header(
HttpHeaders.ContentDisposition,
ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "ktor_logo.png")
.toString()
)
call.respondFile(file)
}
get("/downloadFromPath") {
val filePath = Path.of("files/file.txt")
call.response.header(
HttpHeaders.ContentDisposition,
ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "file.txt")
.toString()
)
call.respond(LocalPathContent(filePath))
}
}このサンプルでは、2つのプラグインを使用していることに注意してください。
PartialContent: サーバーがRangeヘッダーを含むリクエストに応答し、コンテンツの一部のみを送信できるようにします。AutoHeadResponse:GETが定義されているすべてのルートに対して、自動的にHEADリクエストに応答する機能を提供します。これにより、クライアントアプリケーションはContent-Lengthヘッダー値を読み取ることでファイルサイズを判断できます。
完全なコードサンプルについては、download-file を参照してください。
リソース
call.respondResource() メソッドを使用して、から単一のリソースを提供できます。 このメソッドはリソースへのパスを受け取り、次のように構築されたレスポンスを送信します。 リソースストリームからレスポンスボディを読み取り、ファイル拡張子から Content-Type ヘッダーを導出します。
以下の例は、ルートハンドラーでのメソッド呼び出しを示しています。
routing {
get("/resource") {
call.respondResource("public/index.html")
}
}上記の例では、リソースの拡張子が .html であるため、レスポンスには Content-Type: text/html ヘッダーが含まれます。 便宜上、リソースの場所の構成要素(相対パスとパッケージ)を、第1パラメータと第2パラメータを通じて個別に渡すことができます。 次の例では、リクエストされたパスに基づいて assets パッケージ配下のリソースを解決します。
get("/assets/{rest-path...}") {
var path = call.parameters["rest-path"]
if (path.isNullOrEmpty()) {
path = "index.html"
}
try {
call.respondResource(path, "assets") {
application.log.info(this.contentType.toString())
}
} catch (_: IllegalArgumentException) {
call.respond(HttpStatusCode.NotFound)
}
}/assets プレフィックス以降のリクエストパスが空または / の場合、ハンドラーはデフォルトの index.html リソースを使用して応答します。指定されたパスにリソースが見つからない場合、IllegalArgumentException がスローされます。 前のコードスニペットは、より一般的なソリューション、つまり staticResources() メソッドを使用してパッケージからリソースを提供する方法を模倣したものです。
生のペイロード
生のボディペイロードを送信するには、call.respondBytes() 関数を使用します。
レスポンスパラメータの設定
ステータスコード
レスポンスのステータスコードを設定するには、定義済みのステータスコード値を指定して ApplicationResponse.status() 関数を呼び出します。
get("/") {
call.response.status(HttpStatusCode.OK)
}カスタムのステータス値を指定することもできます。
get("/") {
call.response.status(HttpStatusCode(418, "I'm a tea pot"))
}すべてのペイロード送信関数には、ステータスコードを受け取るオーバーロードも用意されています。
コンテンツタイプ
ContentNegotiation プラグインがインストールされている場合、Ktorはコンテンツタイプを自動的に選択します。必要に応じて、対応するパラメータを渡すことで手動でコンテンツタイプを指定できます。
以下の例では、call.respondText() 関数がパラメータとして ContentType.Text.Plain を受け取っています。
get("/") {
call.respondText("Hello, world!", ContentType.Text.Plain, HttpStatusCode.OK)
}ヘッダー
レスポンスにヘッダーを追加するには、いくつかの方法があります。
ApplicationResponse.headersコレクションを変更する:kotlinget("/") { call.response.headers.append(HttpHeaders.ETag, "7c876b7e") // 同じヘッダーに複数の値を設定する場合 call.response.headers.appendAll("X-Custom-Header" to listOf("value1", "value2")) }ApplicationResponse.header()関数を使用する:kotlinget("/") { call.response.header(HttpHeaders.ETag, "7c876b7e") }ApplicationResponse.etagやApplicationResponse.linkなど、特定のヘッダー用の便利な関数を使用する:kotlinget("/") { call.response.etag("7c876b7e") }生の文字列名を渡してカスタムヘッダーを追加する:
kotlinget("/") { call.response.header("Custom-Header", "Some value") }
標準の
ServerおよびDateヘッダーを自動的に含めるには、DefaultHeaders プラグインをインストールしてください。
クッキー
レスポンスで送信されるクッキーを構成するには、ApplicationResponse.cookies プロパティを使用します。
get("/") {
call.response.cookies.append("yummy_cookie", "choco")
}Ktorはクッキーを使用してセッションを処理する機能も提供しています。詳細は セッション を参照してください。
リダイレクト
リダイレクトレスポンスを生成するには、call.respondRedirect() 関数を使用します。
get("/") {
call.respondRedirect("/moved", permanent = true)
}
get("/moved") {
call.respondText("Moved content")
}