HTTPレスポンスの生成

Estimated reading time: 3 minutes

ルーティングのハンドリングまたは直接パイプラインをインターセプトする際に、 ApplicationCallから実行時のコンテキスト情報を取得できます。 callresponseと呼ばれるプロパティを持っており、それを使うことでレスポンスの送信を行うことができます。

また、call自体もレスポンスを操作するような便利なプロパティやメソッドをいくつか持っています。

目次:

コンテキスト情報

Routing機能を利用する際には、 ルートハンドラ内でcallプロパティにアクセスすることができるようになります。

routing {
    get("/") {
        call.respondText("Request uri: ${call.request.uri}")
    } 
}

リクエストのインターセプトを行う際に、intercept内のlambda関数がcallプロパティを持っており、それを利用することもできます。

intercept(ApplicationCallPipeline.Call) { 
    if (call.request.uri == "/") {
        call.respondText("Test String")
    }
}

HTTPヘッダーとステータスの操作

HTTPステータス、ヘッダー、Cookie、payloadなど、レスポンスがどのように生成されるのかを操作することができます。

HTTPリクエストとレスポンスはシークできないストリームであるため、 一度レスポンスpayload/コンテンツを送信し始めると、 ステータスやヘッダーが送信され、 ステータスやヘッダーやCookieを変更することができなくなる点にご注意ください。

responseの一部として、以下のような内部contextにもアクセスできます。

  • val call: ApplicationCall = response.call
  • val pipeline: ApplicationSendPipeline = response.pipeline

ヘッダー:

  • val headers: ResponseHeaders = response.headers

Set-Cookieヘッダーをセットするための便利なcookiesインスタンス:

  • val cookies: ResponseCookies = response.cookies

HTTP Statusの取得と変更:

  • response.status(HttpStatusCode.OK) - 事前に定義されたHTTPステータスコードの設定
  • response.status(HttpStatusCode(418, "I'm a tea pot")) - カスタムのHTTPステータスコードの設定
  • val status: HttpStatusCode? = response.status() - 現在設定されているHTTPステータスコードの取得

  • response.contentType(ContentType.Text.Plain.withCharset(Charsets.UTF_8)) - 定義されている型を使いContent-Typeを設定(ContentType.Application.JsonにおいてはUTF-8がデフォルトの文字コードです)
  • response.contentType("application/json; charset=UTF-8") - 定義されている型を使わずContent-Typeを設定

カスタムヘッダー:

  • response.header("X-My-Header", "my value") - カスタムヘッダーの追加
  • response.header("X-My-Times", 1000) - カスタムヘッダーの追加
  • response.header("X-My-Times", 1000L) - カスタムヘッダーの追加
  • response.header("X-My-Date", Instant.EPOCH) - カスタムヘッダーの追加

通常インフラストラクチャによって設定されるヘッダーをセットする便利なメソッド群:

  • response.etag("33a64df551425fcc55e4d42a148795d9f25f89d4") - キャッシュのためETagの設定
  • response.lastModified(ZonedDateTime.now()) - Last-Modifiedヘッダーの設定
  • response.contentLength(1024L) - Content-Lengthの設定。一般的にはpayload送信時に自動で付与されるものです。
  • response.cacheControl(CacheControl.NoCache(CacheControl.Visibility.Private)) - 定義されている型を使いCache-Controlの設定
  • response.expires(LocalDateTime.now()) - Expiresヘッダーの設定
  • response.contentRange(1024L until 2048L, 4096L) - Content-Rangeヘッダーの設定(PartialContent機能をご覧ください)

HTTP/2 pushingとHTTP/1 Link ヘッダー

callはpushingをサポートしています。

  • HTTP/2において、push機能が使えます。
  • HTTP/1.2において、Linkヘッダーをヒントとして付与できます。
routing {
    get("/") {
        call.push("/style.css")
    }
}

Pushingはリクエストからページの表示までの間の時間を削減します。 しかし、コンテンツを事前に送信することはクライアントにすでにキャッシュされているコンテンツを送信することになるかもしれないので用心してください。

リダイレクト

respondRedirectメソッドを使うことでリダイレクトレスポンスを簡単に生成することができます。 301 Moved Permanently302 FoundによるリダイレクトをLocationヘッダーを使って行います。

call.respondRedirect("/moved/here", permanent = true)

この関数が実行されると、残りの関数も実行されることに注意してください。 したがって、ガード句内で利用する場合は、ハンドラーの残りの部分を継続して処理しないよう関数からreturnする必要があります。 例外を投げることによって制御フローを止めるリダイレクトを行いたい場合は、ステータスページのサンプルをご覧ください。

レスポンスコンテンツの送信

ジェネリクスのコンテンツを送信(コンテントネゴシエーションと互換性があります)

  • call.respond(MyDataClass("hello", "world")) - コンテンツネゴシエーションセクションをご確認ください
  • call.respond(HttpStatusCode.NotFound, MyDataClass("hello", "world")) - ステータスコードを指定しpayloadを送信するのを1回の呼び出しで行います。ステータスページをご確認ください。

プレーンテキストの送信:

  • call.respondText("text") - 単なる文字列をBodyに入れる
  • call.respondText("p { background: red; }", contentType = ContentType.Text.CSS, status = HttpStatusCode.OK) { ... } - ContentType、HTTPステータスを指定しテキストを送信し、OutgoingContentの設定を行う
  • call.respondText { "string" } - suspendプロバイダで文字列を返す
  • call.respondText(contentType = ..., status = ...) { "string" } - suspendプロバイダで文字列を返す
  • call.respond(TextContent("{}", ContentType.Application.Json)) - Content-Typeに文字コードを追加し文字列を返す

byte arrayの送信:

  • call.respondBytes(byteArrayOf(1, 2, 3)) - binaryのbodyでByteArray

ファイルの送信:

  • call.respondFile(File("/path/to/file")) - ファイルの送信
  • call.respondFile(File("basedir"), "filename") { ... } - ファイルを送信し、OutgoingContentを設定

URL-encoded form(application/x-www-form-urlencoded)の送信:

  • Parameters.formUrlEncodeが利用できます.詳細はUtilitiesページをご確認ください。

リクエストパラメータに基づいてファイルを送信するとき、入力のバリデーションと制限方法について特に注意してください。

Writerを使いchunked contentを送信:

  • call.respondWrite { write("hello"); write("world") } - writerを使いテキストを送信。HTML DSLと一緒に使えます。
  • call.respondWrite(contentType = ..., status = ...) { write("hello"); write("world") } - writerを使いテキストを送信し、contentTypeとステータスを指定します。

WriteChannelContentを使いチャンク内の任意のデータを送信:

call.respond(object : OutgoingContent.WriteChannelContent() {
    override val contentType = ContentType.Application.OctetStream
    override suspend fun writeTo(channel: ByteWriteChannel) {
        channel.writeFully(byteArray1)
        channel.writeFully(byteArray2)
        // ...
    }
})

リクエストのため、デフォルトのcontentTypeを指定:

  • call.defaultTextContentType(contentType: ContentType?): ContentType

レスポンス設定のためのOutgoingContentインターフェース:

class OutgoingContent {
    val contentType: ContentType? get() = null // * Specifies [ContentType] for this resource.
    val contentLength: Long? get() = null // Specifies content length in bytes for this resource. - If null, the resources will be sent as `Transfer-Encoding: chunked` 
    val status: HttpStatusCode? // Status code to set when sending this content
    val headers: Headers // Headers to set when sending this content
    fun <T : Any> getProperty(key: AttributeKey<T>): T? = extensionProperties?.getOrNull(key) // Gets an extension property for this content
    fun <T : Any> setProperty(key: AttributeKey<T>, value: T?) // Sets an extension property for this content
}

ファイルをダウンロード可能にする

Content-Dispositionヘッダーを付与することでファイルをダウンロード可能にすることができます。

定義されている型を使わない方法だと、以下のように使えます:

call.response.header(HttpHeaders.ContentDisposition, "attachment; filename=\"myfilename.bin\"")

しかしKtorは定義されている方も提供しており、その方法だと適切なエスケープとヘッダーの生成を行ってくれます:

call.response.header(HttpHeaders.ContentDisposition, ContentDisposition.Attachment.withParameter(ContentDisposition.Parameters.FileName, "myfilename.bin").toString())

コンテントネゴシエーション

コンテントネゴシエーションのためのプラグインの設定をするとき、 パイプラインはcall.respondメソッドのための追加の型を受け入れるかもしれません。

HTMLをDSLで送信

Ktorはオプショナルな機能としてHTMLコンテンツをDSLを使って送信する機能を持ちます.

HTMLをFreeMarkerで送信

Ktorはオプショナルな機能としてHTMLコンテンツをFreeMarkerを使って送信する機能を持ちます.

JSONをdata classを使って送信

Ktorはオプショナルな機能としてJSONコンテンツをContentNegotiationを使って送信する機能を持ちます.