發出請求
程式碼範例: client-configure-request
在配置客戶端後,您就可以開始發送 HTTP 請求。執行此操作的主要方式是使用接受 URL 作為參數的 .request()
函數。在這個函數中,您可以配置各種請求參數:
- 指定 HTTP 方法,例如
GET
、POST
、PUT
、DELETE
、HEAD
、OPTIONS
或PATCH
。 - 將 URL 配置為字串,或單獨配置其組件(例如網域、路徑和查詢參數)。
- 使用 Unix 網域通訊端。
- 新增標頭和 Cookie。
- 包含請求主體 – 例如,純文字、資料物件或表單參數。
這些參數由 HttpRequestBuilder
類別公開。
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 方法:
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()
函數傳遞單個路徑段。
client.get("https://ktor.io") {
url {
appendPathSegments("docs", "welcome.html")
}
}
預設情況下,appendPathSegments
會編碼路徑段。若要禁用編碼,請改用 appendEncodedPathSegments()
。
查詢參數
若要新增 查詢字串 參數,請使用 URLBuilder.parameters
屬性:
client.get("https://ktor.io") {
url {
parameters.append("token", "abc123")
}
}
預設情況下,parameters
會編碼查詢參數。若要禁用編碼,請改用 encodedParameters()
。
即使沒有查詢參數,
trailingQuery
屬性也可用於保留?
字元。
URL 片段
雜湊符號 #
在 URL 結尾附近引入可選的片段。您可以使用 fragment
屬性配置 URL 片段。
client.get("https://ktor.io") {
url {
fragment = "some_anchor"
}
}
預設情況下,fragment
會編碼 URL 片段。若要禁用編碼,請改用 encodedFragment()
。
指定 Unix 網域通訊端
Unix 網域通訊端僅在 CIO 引擎中受支持。若要將 Unix 通訊端與 Ktor 伺服器一起使用,請相應地配置伺服器。
若要向監聽 Unix 網域通訊端的伺服器發送請求,請在使用 CIO 客戶端時呼叫 unixSocket()
函數:
val client = HttpClient(CIO)
val response: HttpResponse = client.get("/") {
unixSocket("/tmp/test-unix-socket-ktor.sock")
}
您還可以將 Unix 網域通訊端配置為預設請求的一部分。
設定請求參數
您可以指定各種請求參數,包括 HTTP 方法、標頭和 Cookie。如果您需要為特定客戶端的所有請求配置預設參數,請使用 DefaultRequest
插件。
標頭
您可以透過幾種方式將標頭新增至請求中:
新增多個標頭
headers
函數允許您一次新增多個標頭:
client.get("https://ktor.io") {
headers {
append(HttpHeaders.Accept, "text/html")
append(HttpHeaders.Authorization, "abc123")
append(HttpHeaders.UserAgent, "ktor client")
}
}
新增單個標頭
header
函數允許您附加單個標頭。
使用 basicAuth
或 bearerAuth
進行授權
basicAuth
和 bearerAuth
函數會新增帶有相應 HTTP 方案的 Authorization
標頭。
如需進階身份驗證配置,請參閱Ktor 客戶端中的身份驗證和授權。
Cookie
若要發送 Cookie,請使用 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
插件,它允許您在呼叫之間保留 Cookie。如果安裝了此插件,則會忽略使用 cookie()
函數新增的 Cookie。
設定請求主體
若要設定請求主體,請呼叫 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"))
}
有關更多資訊,請參閱Ktor 客戶端中的內容協商和序列化。
表單參數
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
構造函數也允許您傳遞 boundary 值。
對於這兩種方法,您都需要使用 formData {}
函數建構表單資料。
使用 .submitFormWithBinaryData()
.submitFormWithBinaryData()
函數會自動生成一個 boundary,適用於檔案內容小到足以使用 .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("已發送 $bytesSentTotal 位元組,共 $contentLength 位元組")
}
}
在多平台專案中,您可以將 SystemFileSystem.source()
與 InputProvider
一起使用:
InputProvider { SystemFileSystem.source(Path("ktor_logo.png")).buffered() }
您還可以手動建構具有自訂 boundary 和內容類型的 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 {
// 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
,可用於取消正在執行的協程:
import kotlinx.coroutines.*
val client = HttpClient(CIO)
val job = launch {
val requestContent: String = client.get("http://localhost:8080")
}
job.cancel()
有關更多詳細資訊,請參閱取消和逾時。