The basic usage is super simple: you just have to instantiate an HttpClient instance,
specifying an engine, for example
Apache,
OkHttp,
Android,
Ios,
Jetty
or CIO,
and start making requests using one of the many convenience methods available.
You can omit the engine, and Ktor will choose an engine among the ones that are available from the included artifacts using a ServiceLoader on the JVM, or the default implementation in the other platforms.
First you need to instantiate the client:
val client = HttpClient(Apache)
Then, to perform a GET request fully reading a String:
val htmlContent = client.get<String>("https://en.wikipedia.org/wiki/Main_Page")
And in the case you are interested in the raw bits, you can read a ByteArray:
val bytes: ByteArray = client.call("http://127.0.0.1:8080/").response.readBytes()
It is possible to customize the request a lot and
to stream the request and response payloads, but you can also just call a convenience
extension method like HttpClient.get to do a GET request to receive
the specified type directly (for example String).
After you finish working with the client, it should be closed in order to properly stop the underlying engine.
client.close()
If you want to use a client to make only one request consider use-ing it. The client will be automatically closed once the passed block has been executed:
val bytes = HttpClient(Apache).use { client ->
client.call("http://127.0.0.1:8080/").response.readBytes()
}
We cannot live only on get requests, Ktor allows you to build complex requests with any of the HTTP verbs, with the flexibility to process responses in many ways.
call methodThe HttpClient call method, returns an HttpClientCall and allows you to perform
simple untyped requests.
You can read the content using response: HttpResponse.
For further information, check out the receiving content using HttpResponse section.
val call = client.call("http://127.0.0.1:8080/") {
method = HttpMethod.Get
}
println(call.response.readText())
request methodIn addition to call, there is a request method for performing a typed request,
receiving a specific type like String, HttpResponse, or an arbitrary class.
You have to specify the URL and the method when building the request.
val call = client.request<String> {
url(URL("http://127.0.0.1:8080/"))
method = HttpMethod.Get
}
get, post, put, delete, patch, head and options methodsSimilar to request, there are several extension methods to perform requests
with the most common HTTP verbs: GET, POST, PUT, DELETE, PATCH, HEAD and OPTIONS.
val text = client.post<String>("http://127.0.0.1:8080/")
When calling request methods, you can provide a lambda to build the request
parameters like the URL, the HTTP method (verb), the body, or the headers.
The HttpRequestBuilder looks like this:
class HttpRequestBuilder : HttpMessageBuilder {
val url: URLBuilder
var method: HttpMethod
val headers: HeadersBuilder
var body: Any = EmptyContent
val executionContext: CompletableDeferred<Unit>
fun header(key: String, value: String)
fun headers(block: HeadersBuilder.() -> Unit)
fun url(block: URLBuilder.(URLBuilder) -> Unit)
}
The HttpClient class only offers some basic functionality, and all the methods for building requests are exposed as extensions.
You can check the standard available HttpClient build extension methods.
submitForm and submitFormWithBinaryData methodsThere are a couple of convenience extension methods for submitting form information.
The submitForm method:
submitForm(formData: Parameters = Parameters.Empty, encodeInQuery: Boolean = false, block: HttpRequestBuilder.() -> Unit = {})
It allows to do a request with the Parameters encoded in the querystring (GET by default),
or to do a request with the Parameters encoded as multipart (POST by default) depending on the encodeInQuery parameter.
The submitFormWithBinaryData method:
submitFormWithBinaryData(formData: List<PartData>, block: HttpRequestBuilder.() -> Unit = {}): T
It allows to generate a multipart POST request from a list of PartData.
PartData can be PartData.FormItem, PartData.BinaryItem or PartData.FileItem.
To build a list of PartData, you can use the formData builder:
val data: List<PartData> = formData {
// Can append: String, Number, ByteArray and Input.
append("hello", "world")
append("number", 10)
append("ba", byteArrayOf(1, 2, 3, 4))
append("input", inputStream.asInput())
// Allow to set headers to the part:
append("hello", "world", headersOf("X-My-Header" to "MyValue"))
}
When building requests with HttpRequestBuilder, you can set custom headers.
There is a final property val headers: HeadersBuilder that inherits from StringValuesBuilder.
You can add or remove headers using it, or with the header convenience methods.
// this : HttpMessageBuilder
// Convenience method to add a header
header("My-Custom-Header", "HeaderValue")
// Calls methods from the headers: HeadersBuilder to manipulate the headers
headers.clear()
headers.append("My-Custom-Header", "HeaderValue")
headers.appendAll("My-Custom-Header", listOf("HeaderValue1", "HeaderValue2"))
headers.remove("My-Custom-Header")
// Applies the headers with the `headers` convenience method
headers { // this: HeadersBuilder
clear()
append("My-Custom-Header", "HeaderValue")
appendAll("My-Custom-Header", listOf("HeaderValue1", "HeaderValue2"))
remove("My-Custom-Header")
}
For POST and PUT requests, you can set the body property:
client.post<Unit> {
url(URL("http://127.0.0.1:8080/"))
body = // ...
}
The HttpRequestBuilder.body property can be a subtype of OutgoingContent as well as a String instance:
body = "HELLO WORLD!"body = TextContent("HELLO WORLD!", ContentType.Text.Plain)body = ByteArrayContent("HELLO WORLD!".toByteArray(Charsets.UTF_8))body = LocalFileContent(File("build.gradle"))body = JarFileContent(File("myjar.jar"), "test.txt", ContentType.fromFileExtension("txt").first())body = URIFileContent(URL("https://en.wikipedia.org/wiki/Main_Page"))If you install the JsonFeature, and set the content type to application/json
you can use arbitrary instances as the body, and they will be serialized as JSON:
data class HelloWorld(val hello: String)
val client = HttpClient(Apache) {
install(JsonFeature) {
serializer = GsonSerializer {
// Configurable .GsonBuilder
serializeNulls()
disableHtmlEscaping()
}
}
}
client.post<Unit> {
url(URL("http://127.0.0.1:8080/"))
body = HelloWorld(hello = "world")
}
Alternatively (using the integrated JsonSerializer):
val json = io.ktor.client.features.json.defaultSerializer()
client.post<Unit>() {
url(URL("http://127.0.0.1:8080/"))
body = json.write(HelloWorld(hello = "world")) // Generates an OutgoingContent
}
Or using Jackson (JVM only):
val json = jacksonObjectMapper()
client.post<Unit> {
url(URL("http://127.0.0.1:8080/"))
body = TextContent(json.writeValueAsString(userData), contentType = ContentType.Application.Json)
}
Remember that your classes must be top-level to be recognized by Gson.
If you try to send a class that is inside a function, the feature will send a null.
Ktor HTTP Client has support for making MultiPart requests.
The idea is to use the MultiPartFormDataContent(parts: List<PartData>) as OutgoingContent for the body of the request.
The easiest way is to use the submitFormWithBinaryData method.
Alternatively you can set the body directly:
val request = client.request {
method = HttpMethod.Post
body = FormDataContent(formData {
append("key", "value")
})
}