Skip to content

リクエストの送信

クライアントの設定が完了したら、HTTPリクエストの送信を開始できます。これを行う主な方法は、URLをパラメータとして受け取る .request() 関数を使用することです。この関数内では、さまざまなリクエストパラメータを設定できます。

  • GETPOSTPUTDELETEHEADOPTIONSPATCHなどのHTTPメソッドを指定する。
  • URLを文字列として設定するか、その構成要素(ドメイン、パス、クエリパラメータなど)を個別に設定する。
  • Unixドメインソケットを使用する。
  • ヘッダーとクッキーを追加する。
  • リクエストボディ(プレーンテキスト、データオブジェクト、フォームパラメータなど)を含める。

これらのパラメータは HttpRequestBuilder クラスによって提供されます。

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*

val response: HttpResponse = client.request("https://ktor.io/") {
  // HttpRequestBuilderによって公開されているリクエストパラメータを設定する
}

.request()関数は、レスポンスをHttpResponseオブジェクトとして返します。HttpResponseは、文字列やJSONオブジェクトなどのさまざまな形式でレスポンスボディを取得するために必要なAPIや、ステータスコード、コンテンツタイプ、ヘッダーなどのレスポンスパラメータを取得するためのAPIを提供します。詳細については、レスポンスの受信を参照してください。

.request()はサスペンド関数(suspending function)であるため、コルーチンまたは別のサスペンド関数内から呼び出す必要があります。サスペンド関数の詳細については、コルーチンの基本を参照してください。

HTTPメソッドの指定

.request()関数を呼び出す際、methodプロパティを使用して目的のHTTPメソッドを指定できます。

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*

val response: HttpResponse = client.request("https://ktor.io/") {
    method = HttpMethod.Get
}

.request()に加えて、HttpClient.get().post().put() などの基本的なHTTPメソッドのための特定の関数を提供しています。上記の例は、.get()関数を使用して簡略化できます。

kotlin
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

どちらの例でも、リクエストURLは文字列として指定されています。HttpRequestBuilderを使用して、URLの構成要素を個別に設定することもできます。

リクエストURLの指定

Ktorクライアントでは、いくつかの方法でリクエストURLを設定できます。

URL文字列全体を渡す

kotlin
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

URLコンポーネントを個別に設定する

kotlin
client.get {
    url {
        protocol = URLProtocol.HTTPS
        host = "ktor.io"
        path("docs/welcome.html")
    }
}

この場合、HttpRequestBuilderによって提供されるurlパラメータが使用されます。これはURLBuilderのインスタンスを受け取り、複雑なURLを構築するためのより柔軟な方法を提供します。

すべてのリクエストに対してベースURLを設定するには、DefaultRequestプラグインを使用します。

パスセグメント

前の例では、URLパス全体がURLBuilder.pathプロパティを使用して指定されていました。あるいは、appendPathSegments()関数を使用して個々のパスセグメントを渡すこともできます。

kotlin
client.get("https://ktor.io") {
    url {
        appendPathSegments("docs", "welcome.html")
    }
}

デフォルトでは、appendPathSegmentsはパスセグメントをエンコードします。エンコードを無効にするには、代わりにappendEncodedPathSegments()を使用してください。

クエリパラメータ

クエリ文字列パラメータを追加するには、URLBuilder.parametersプロパティを使用します。
kotlin
client.get("https://ktor.io") {
    url {
        parameters.append("token", "abc123")
    }
}

デフォルトでは、parametersはクエリパラメータをエンコードします。エンコードを無効にするには、代わりにencodedParameters()を使用してください。

trailingQueryプロパティを使用すると、クエリパラメータがない場合でも?文字を保持できます。

URLフラグメント

ハッシュ記号#は、URLの末尾付近にオプションのフラグメントを導入します。fragmentプロパティを使用してURLフラグメントを設定できます。

kotlin
client.get("https://ktor.io") {
    url {
        fragment = "some_anchor"
    }
}

デフォルトでは、fragmentはURLフラグメントをエンコードします。エンコードを無効にするには、代わりにencodedFragment()を使用してください。

Unixドメインソケットの指定

UnixドメインソケットはCIOエンジンでのみサポートされています。 KtorサーバーでUnixソケットを使用するには、それに応じてサーバーを設定してください。

Unixドメインソケットをリッスンしているサーバーにリクエストを送信するには、CIOクライアントを使用する際にunixSocket()関数を呼び出します。

kotlin
val client = HttpClient(CIO)

val response: HttpResponse = client.get("/") {
    unixSocket("/tmp/test-unix-socket-ktor.sock")
}

Unixドメインソケットは、デフォルトリクエストの一部としても設定できます。

リクエストパラメータの設定

HTTPメソッド、ヘッダー、クッキーなど、さまざまなリクエストパラメータを指定できます。特定のクライアントのすべてのリクエストに対してデフォルトのパラメータを設定する必要がある場合は、DefaultRequestプラグインを使用してください。

ヘッダー

リクエストにヘッダーを追加するには、いくつかの方法があります。

複数のヘッダーを追加する

headers関数を使用すると、複数のヘッダーを一度に追加できます。

kotlin
client.get("https://ktor.io") {
    headers {
        append(HttpHeaders.Accept, "text/html")
        append(HttpHeaders.Authorization, "abc123")
        append(HttpHeaders.UserAgent, "ktor client")
    }
}

また、appendAll()関数をMapまたはvararg Pairと共に使用して、複数のヘッダーを便利に追加することもできます。

kotlin
        client.get("https://ktor.io") {
            headers {
                // vararg Pairsを使用する場合
                appendAll(
                    HttpHeaders.Accept to "text/html",
                    HttpHeaders.Authorization to "abc123"
                )

                // Mapを使用する場合
                appendAll(mapOf("foo" to "bar", "baz" to "qux"))
                appendAll(mapOf("test" to listOf("1", "2", "3")))

                // 複数の値を持つカスタムヘッダーを使用する場合
                appendAll("X-Custom-Header" to listOf("val1", "val2"))
            }
        }

単一のヘッダーを追加する

header関数を使用すると、単一のヘッダーを追加できます。

認証に basicAuth または bearerAuth を使用する

basicAuthおよびbearerAuth関数は、対応するHTTPスキームを使用してAuthorizationヘッダーを追加します。

高度な認証設定については、Ktor Clientにおける認証と認可を参照してください。

クッキー

クッキーを送信するには、cookie()関数を使用します。

kotlin
client.get("https://ktor.io") {
    cookie(name = "user_name", value = "jetbrains", expires = GMTDate(
        seconds = 0,
        minutes = 0,
        hours = 10,
        dayOfMonth = 1,
        month = Month.APRIL,
        year = 2023
    ))
}

Ktorは、呼び出し間でクッキーを保持できるHttpCookiesプラグインも提供しています。このプラグインがインストールされている場合、cookie()関数を使用して追加されたクッキーは無視されます。

リクエストボディの設定

リクエストボディを設定するには、HttpRequestBuilderによって提供されるsetBody()関数を呼び出します。この関数は、プレーンテキスト、任意のクラスインスタンス、フォームデータ、バイト配列など、さまざまなタイプのペイロードを受け取ります。

テキスト

プレーンテキストをボディとして送信するには、次のように実装できます。

kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*

val response: HttpResponse = client.post("http://localhost:8080/post") {
    setBody("Body content")
}

オブジェクト

ContentNegotiationプラグインを有効にすると、リクエストボディ内のクラスインスタンスをJSONとして送信できます。これを行うには、クラスインスタンスをsetBody()関数に渡し、contentType()関数を使用してコンテンツタイプをapplication/jsonに設定します。

kotlin
val response: HttpResponse = client.post("http://localhost:8080/customer") {
    contentType(ContentType.Application.Json)
    setBody(Customer(3, "Jet", "Brains"))
}

詳細については、Ktor Clientにおけるコンテンツネゴシエーションとシリアライズを参照してください。

フォームパラメータ

Ktorクライアントは、application/x-www-form-urlencodedタイプでフォームパラメータを送信するためのsubmitForm()関数を提供しています。次の例にその使用方法を示します。

kotlin
val client = HttpClient(CIO)
val response: HttpResponse = client.submitForm(
    url = "http://localhost:8080/signup",
    formParameters = parameters {
        append("username", "JetBrains")
        append("email", "[email protected]")
        append("password", "foobar")
        append("confirmation", "foobar")
    }
)
  • url はリクエストを行うためのURLを指定します。
  • formParametersparametersを使用して構築されたフォームパラメータのセットです。

完全な例については、client-submit-formを参照してください。

URLにエンコードされたフォームパラメータを送信するには、encodeInQuerytrueに設定してください。

ファイルのアップロード

フォームと共にファイルを送信する必要がある場合は、次のアプローチを使用できます。

  • .submitFormWithBinaryData()関数を使用する。この場合、バウンダリ(boundary)は自動的に生成されます。
  • post関数を呼び出し、MultiPartFormDataContentインスタンスをsetBody関数に渡す。MultiPartFormDataContentコンストラクタでは、バウンダリ値を渡すこともできます。

どちらのアプローチでも、formData {}関数を使用してフォームデータを構築する必要があります。

.submitFormWithBinaryData() を使用する

.submitFormWithBinaryData()関数は自動的にバウンダリを生成し、ファイルの内容が十分に小さく、.readBytes()を使用して安全にメモリに読み込める単純なユースケースに適しています。

kotlin
        val client = HttpClient(CIO)

        val response: HttpResponse = client.submitFormWithBinaryData(
            url = "http://localhost:8080/upload",
            formData = formData {
                append("description", "Ktor logo")
                append("image", File("ktor_logo.png").readBytes(), Headers.build {
                    append(HttpHeaders.ContentType, "image/png")
                    append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                })
            }
        )

完全な例については、client-uploadを参照してください。

MultiPartFormDataContent を使用する

大容量または動的なコンテンツを効率的にストリーミングするには、InputProviderを備えたMultiPartFormDataContentを使用できます。InputProviderを使用すると、ファイルデータを完全にメモリに読み込むのではなく、バッファ付きストリームとして提供できるため、大きなファイルに適しています。MultiPartFormDataContentを使用すると、onUploadコールバックを使用してアップロードの進行状況を監視することもできます。

kotlin
        val client = HttpClient(CIO)

        val file = File("ktor_logo.png")

        val response: HttpResponse = client.post("http://localhost:8080/upload") {
            setBody(
                MultiPartFormDataContent(
                    formData {
                        append("description", "Ktor logo")
                        append(
                            "image",
                            InputProvider { file.inputStream().asInput().buffered() },
                            Headers.build {
                                append(HttpHeaders.ContentType, "image/png")
                                append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                            }
                        )
                    },
                    boundary = "WebAppBoundary"
                )
            )
            onUpload { bytesSentTotal, contentLength ->
                println("Sent $bytesSentTotal bytes from $contentLength")
            }
        }

マルチプラットフォームプロジェクトでは、InputProviderSystemFileSystem.source()を使用できます。

kotlin
InputProvider { SystemFileSystem.source(Path("ktor_logo.png")).buffered() }

また、カスタムバウンダリとコンテンツタイプを使用して、MultiPartFormDataContentを手動で構築することもできます。

kotlin
fun customMultiPartMixedDataContent(parts: List<PartData>): MultiPartFormDataContent {
    val boundary = "WebAppBoundary"
    val contentType = ContentType.MultiPart.Mixed.withParameter("boundary", boundary)
    return MultiPartFormDataContent(parts, boundary, contentType)
}

完全な例については、client-upload-progressを参照してください。

バイナリデータ

application/octet-streamコンテンツタイプでバイナリデータを送信するには、ByteReadChannelインスタンスをsetBody()関数に渡します。 たとえば、File.readChannel()関数を使用してファイルの読み取りチャネルを開くことができます。

kotlin
val response = client.post("http://0.0.0.0:8080/upload") {
    setBody(File("ktor_logo.png").readChannel())
}

完全な例については、client-upload-binary-dataを参照してください。

並列リクエスト

デフォルトでは、複数のリクエストを順番に送信する場合、クライアントは前のリクエストが完了するまで各呼び出しを一時停止(サスペンド)します。複数のリクエストを並行して実行するには、 launch() または async() 関数を使用します。次の例は、async()を使用して2つのリクエストを並列に実行する方法を示しています。

kotlin
coroutineScope {
    // 並列リクエスト
    val firstRequest: Deferred<String> = async { client.get("http://localhost:8080/path1").bodyAsText() }
    val secondRequest: Deferred<String> = async { client.get("http://localhost:8080/path2").bodyAsText() }
    val firstRequestContent = firstRequest.await()
    val secondRequestContent = secondRequest.await()
}

完全な例については、client-parallel-requestsを参照してください。

リクエストのキャンセル

リクエストをキャンセルするには、そのリクエストを実行しているコルーチンをキャンセルします。 launch() 関数は、実行中のコルーチンをキャンセルするために使用できるJobを返します。

kotlin
import kotlinx.coroutines.*

val client = HttpClient(CIO)
val job = launch {
    val requestContent: String = client.get("http://localhost:8080")
}
job.cancel()

詳細については、Cancellation and timeoutsを参照してください。