發送請求
程式碼範例: client-configure-request
在配置用戶端之後,您就可以開始發送 HTTP 請求。發送請求的主要方式是使用接受 URL 作為參數的 .request() 函式。在此函式內部,您可以配置各種請求參數:
- 指定 HTTP 方法,例如
GET、POST、PUT、DELETE、HEAD、OPTIONS或PATCH。 - 將 URL 配置為字串,或分別配置其組件(例如網域、路徑和查詢參數)。
- 使用 Unix 網域通訊端(Unix domain socket)。
- 新增標頭和 cookies。
- 包含請求主體 – 例如純文字、資料物件或表單參數。
這些參數由 HttpRequestBuilder 類別提供。
import io.ktor.client.request.*
import io.ktor.client.statement.*
val response: HttpResponse = client.request("https://ktor.io/") {
// 配置 HttpRequestBuilder 提供的請求參數
}.request() 函式會將回應以 HttpResponse 物件形式傳回。HttpResponse 提供了獲取各種格式回應主體(例如字串、JSON 物件等)所需的 API,以及檢索回應參數(例如狀態碼、內容類型和標頭)。如需更多資訊,請參閱 Receiving responses。
.request()是一個掛起函式(suspending function),這意味著它必須從協同程式(coroutine)或其他掛起函式中呼叫。要了解更多關於掛起函式的資訊,請參閱 Coroutines basics。
指定 HTTP 方法
呼叫 .request() 函式時,您可以使用 method 屬性指定所需的 HTTP 方法:
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() 函式簡化:
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")在這兩個範例中,請求 URL 都是以字串形式指定的。您也可以使用 HttpRequestBuilder 分別配置 URL 組件。
指定請求 URL
Ktor 用戶端允許您以多種方式配置請求 URL:
傳遞完整的 URL 字串
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")分別配置 URL 組件
client.get {
url {
protocol = URLProtocol.HTTPS
host = "ktor.io"
path("docs/welcome.html")
}
}在這種情況下,使用了 HttpRequestBuilder 提供的 url 參數。它接受 URLBuilder 的實體,為建置複雜的 URL 提供更多彈性。
若要為所有請求配置基礎 URL,請使用
DefaultRequest外掛程式。
路徑片段
在前面的範例中,整個 URL 路徑是使用 URLBuilder.path 屬性指定的。或者,您可以使用 appendPathSegments() 函式傳遞個別的路徑片段(path segments)。
client.get("https://ktor.io") {
url {
appendPathSegments("docs", "welcome.html")
}
}預設情況下,appendPathSegments 會對路徑片段進行 百分比編碼 (encode)。若要停用編碼,請改用 appendEncodedPathSegments()。
查詢參數
要新增 查詢字串 (query string) 參數,請使用 URLBuilder.parameters 屬性:
client.get("https://ktor.io") {
url {
parameters.append("token", "abc123")
}
}預設情況下,parameters 會對查詢參數進行 編碼。若要停用編碼,請改用 encodedParameters()。
即使沒有查詢參數,也可以使用
trailingQuery屬性來保留?字元。
URL 片段
井字號 # 在 URL 結尾附近引入選用的片段 (fragment)。您可以使用 fragment 屬性配置 URL 片段。
client.get("https://ktor.io") {
url {
fragment = "some_anchor"
}
}預設情況下,fragment 會對 URL 片段進行 編碼。若要停用編碼,請改用 encodedFragment()。
指定 Unix 網域通訊端
Unix 網域通訊端(Unix domain sockets)僅在 CIO 引擎中支援。要將 Unix 通訊端與 Ktor 伺服器配合使用,請相應地配置伺服器。
要向監聽 Unix 網域通訊端的伺服器發送請求,請在呼叫 CIO 用戶端時呼叫 unixSocket() 函式:
val client = HttpClient(CIO)
val response: HttpResponse = client.get("/") {
unixSocket("/tmp/test-unix-socket-ktor.sock")
}您也可以將 Unix 網域通訊端配置為預設請求的一部分。
設定請求參數
您可以指定各種請求參數,包括 HTTP 方法、標頭和 cookies。如果您需要為特定用戶端的所有請求配置預設參數,請使用 DefaultRequest 外掛程式。
標頭
您可以透過幾種方式將標頭新增至請求:
新增多個標頭
headers 函式允許您一次新增多個標頭:
client.get("https://ktor.io") {
headers {
append(HttpHeaders.Accept, "text/html")
append(HttpHeaders.Authorization, "abc123")
append(HttpHeaders.UserAgent, "ktor client")
}
}您也可以將 appendAll() 函式與 Map 或 vararg Pair 配合使用,以方便地新增多個標頭:
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 標頭。
有關進階身分驗證配置,請參閱 Authentication and authorization in Ktor Client。
Cookies
要發送 cookies,請使用 cookie() 函式:
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 外掛程式,允許您在呼叫之間保留 cookies。如果安裝了此外掛程式,使用 cookie() 函式新增的 cookies 將被忽略。
設定請求主體
要設定請求主體,請呼叫 HttpRequestBuilder 提供的 setBody() 函式。此函式接受不同類型的酬載,包括純文字、任意類別實體、表單資料和位元組陣列。
文字
將純文字作為主體發送可以透過以下方式實作:
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:
val response: HttpResponse = client.post("http://localhost:8080/customer") {
contentType(ContentType.Application.Json)
setBody(Customer(3, "Jet", "Brains"))
}如需更多資訊,請參閱 Content negotiation and serialization in Ktor Client。
表單參數
Ktor 用戶端提供了 submitForm() 函式,用於發送 application/x-www-form-urlencoded 類型的表單參數。以下範例示範了其用法:
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建構函式也允許您傳遞邊界值。
對於這兩種方法,您都需要使用 formData {} 函式來建置表單資料。
使用 .submitFormWithBinaryData()
.submitFormWithBinaryData() 函式會自動產生邊界,適用於檔案內容足夠小,可以安全地使用 .readBytes() 讀入記憶體的簡單使用案例。
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
要有效地串流大型或動態內容,您可以將 MultiPartFormDataContent 與 InputProvider 配合使用。InputProvider 允許您以緩衝串流的形式提供檔案資料,而不是將其完整載入記憶體,因此非常適合大型檔案。使用 MultiPartFormDataContent,您還可以使用 onUpload 回調來監控上傳進度。
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")
}
}在多平台(multiplatform)專案中,您可以將 SystemFileSystem.source() 與 InputProvider 配合使用:
InputProvider { SystemFileSystem.source(Path("ktor_logo.png")).buffered() }您也可以手動建構具有自訂邊界和內容類型的 MultiPartFormDataContent:
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() 函式為檔案開啟讀取通道:
val response = client.post("http://0.0.0.0:8080/upload") {
setBody(File("ktor_logo.png").readChannel())
}有關完整範例,請參閱 client-upload-binary-data。
平行請求
預設情況下,當您循序發送多個請求時,用戶端會掛起每個呼叫,直到上一個呼叫完成。要同時執行多個請求,請使用 launch() 或 async() 函式。以下範例示範了如何使用 async() 平行執行兩個請求:
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,可用於取消執行中的協同程式:
import kotlinx.coroutines.*
val client = HttpClient(CIO)
val job = launch {
val requestContent: String = client.get("http://localhost:8080")
}
job.cancel()若要了解更多詳情,請參閱 Cancellation and timeouts。
