Part 2: Accessing the webservice with KTor client


In the last post Part 1 we created a simple webservice for organising tasks.

This time we will access this service using the KTor client features.

Setting up

For this tutorial we will use gradle with kotlin dsl to build the application.

mkdir kotlin_ktor_client
cd kotlin_ktor_client
gradle init --dsl kotlin 
plugins {
    application
    kotlin("jvm") version "1.3.0"
}

application {
    mainClassName = "client.ClientKt"
}

dependencies {
    compile(kotlin("stdlib"))

    compile("io.ktor:ktor-client-core:1.0.0")
    compile("io.ktor:ktor-client-apache:1.0.0")
}

repositories {
    mavenCentral()
    jcenter()
}

// We need this task to package the client into a jar
task("packageClient", type = Jar::class) {
    from(configurations.runtime.map { if (it.isDirectory) it else zipTree(it) })

    manifest {
        attributes["Main-Class"] = "client.ClientKt"
    }
    with(tasks["jar"] as CopySpec)
}

The packageClient task is a kind of hacky but i didn’t find another solution to package all Kotlin dependencies into the jar without it.

Creating the KTor Client

Our client should use both functions of the api. It should be able to show alle tasks and add new tasks.

Therefore if we call the client with no parameters it will just show all tasks. If we start it with parameters it will add a task for each parameter given.

We will use the KTor HttpClient to perform our web requests.

src/main/kotlin/client/Client.kt

...
// Imports are omitted
fun main(args: Array<String>) {
    // Use the HttpClient implementation from apache
    HttpClient(Apache).use {
        when {
            // Query available tasks on empty parameters
            args.isEmpty() -> queryTasks(it)
            // Add tasks on existing parameters
            args.isNotEmpty() -> addTasks(args, it)
        }

    }
}

Query tasks

At first we are going to implement the queryTasks() function. It simply calls the rest endpoint GET /tasks and prints the result.

src/main/kotlin/client/Client.kt

// Below the main() function

private fun queryTasks(client: HttpClient) {
    // Block execution till request is completed
    runBlocking {

        // Execute GET request to /tasks endpoint
        client.get<String>("http://localhost:8080/tasks")
    }.let {tasks ->
        
        // Printout the tasks e.g. [["test"]]
        print(tasks)
    }
}

Add a new task

Analoguos to the taskquery we now implement the addTask() function. It takes an array of Strings and adds a task for each one. This function calls the POST /tasks resource.

src/main/kotlin/client/Client.kt

// Below the main() function

private fun addTasks(args: Array<String>, client: HttpClient) {
    // Block execution till requests are completed
    runBlocking {
        // Add task for each parameter
        args.forEach { task ->
            
            // Execute POST request to /tasks endpoint
            client.post<String>("http://localhost:8080/tasks") { body = task }
        }
    }
}

Putting it together

To start adding and listing tasks we have to start the KTor server from the last tutorial (Part 1).

Start the server using the following gradle command.

gradle run

Now we need a new console window for controlling the client. At first we need to build the clientjar to be able to submit parameter to our application. After this we can start adding and querying tasks.

For this we added the packageClientin our build.gradle.ts.

gradle buildClient

Execute the artifact.

java -jar ./build/libs/kotlin_ktor_client.jar
> [[]]
java -jar ./build/libs/kotlin_ktor_client.jar test
java -jar ./build/libs/kotlin_ktor_client.jar
> [["test"]]

Wrapping up

We created a small web client to connect to our KTor ReST service. The whole project is available on github: kotlin_ktor_client