Skip to content

發出請求

配置客戶端後,您就可以開始發送 HTTP 請求。執行此操作的主要方式是使用接受 URL 作為參數的 .request() 函數。在這個函數中,您可以配置各種請求參數:

  • 指定 HTTP 方法,例如 GETPOSTPUTDELETEHEADOPTIONSPATCH
  • 將 URL 配置為字串,或單獨配置其組件(例如網域、路徑和查詢參數)。
  • 使用 Unix 網域通訊端。
  • 新增標頭和 Cookie。
  • 包含請求主體 – 例如,純文字、資料物件或表單參數。

這些參數由 HttpRequestBuilder 類別公開。

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

val response: HttpResponse = client.request("https://ktor.io/") {
  // Configure request parameters exposed by HttpRequestBuilder
}

.request() 函數以 HttpResponse 物件形式返回回應。HttpResponse 公開了以各種格式(例如字串、JSON 物件等)獲取回應主體以及檢索回應參數(例如狀態碼、內容類型和標頭)所需的 API。有關更多資訊,請參閱接收回應

.request() 是一個暫停函式,這表示它必須從協程或另一個暫停函式中呼叫。要了解有關暫停函式的更多資訊,請參閱協程基礎

指定 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 還提供了用於基本 HTTP 方法的特定函數,例如 .get().post().put()。上面的範例可以使用 .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 引擎中受支持。若要將 Unix 通訊端與 Ktor 伺服器一起使用,請相應地配置伺服器

若要向監聽 Unix 網域通訊端的伺服器發送請求,請在使用 CIO 客戶端時呼叫 unixSocket() 函數:

kotlin
val client = HttpClient(CIO)

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

您還可以將 Unix 網域通訊端配置為預設請求的一部分。

設定請求參數

您可以指定各種請求參數,包括 HTTP 方法、標頭和 Cookie。如果您需要為特定客戶端的所有請求配置預設參數,請使用 DefaultRequest 插件。

標頭

您可以透過幾種方式將標頭新增至請求中:

新增多個標頭

headers 函數允許您一次新增多個標頭:

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

新增單個標頭

header 函數允許您附加單個標頭。

使用 basicAuthbearerAuth 進行授權

basicAuthbearerAuth 函數會新增帶有相應 HTTP 方案的 Authorization 標頭。

如需進階身份驗證配置,請參閱Ktor 客戶端中的身份驗證和授權

Cookie

若要發送 Cookie,請使用 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。如果安裝了此插件,則會忽略使用 cookie() 函數新增的 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 客戶端中的內容協商和序列化

表單參數

Ktor 客戶端提供了 submitForm() 函數,用於發送 application/x-www-form-urlencoded 類型的表單參數。以下範例演示了其用法:

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。
  • formParameters 是使用 parameters 建構的一組表單參數。

如需完整範例,請參閱 client-submit-form

若要發送編碼在 URL 中的表單參數,請將 encodeInQuery 設定為 true

上傳檔案

如果您需要透過表單發送檔案,可以使用以下方法:

  • 使用 .submitFormWithBinaryData() 函數。在這種情況下,將自動生成一個 boundary。
  • 呼叫 post 函數,並將 MultiPartFormDataContent 實例傳遞給 setBody 函數。MultiPartFormDataContent 構造函數也允許您傳遞 boundary 值。

對於這兩種方法,您都需要使用 formData {} 函數建構表單資料。

使用 .submitFormWithBinaryData()

.submitFormWithBinaryData() 函數會自動生成一個 boundary,適用於檔案內容小到足以使用 .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

為了有效率地串流大型或動態內容,您可以將 MultiPartFormDataContentInputProvider 一起使用。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("已發送 $bytesSentTotal 位元組,共 $contentLength 位元組")
            }
        }

在多平台專案中,您可以將 SystemFileSystem.source()InputProvider 一起使用:

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

您還可以手動建構具有自訂 boundary 和內容類型的 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() 並行執行兩個請求:

kotlin
coroutineScope {
    // Parallel requests
    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()

有關更多詳細資訊,請參閱取消和逾時