Ktorを使ってAPIを呼び出す

Ktorを使ってAPIを呼び出す。Server側でAPIを提供するのがメインのようだが、Client側のコードもかけるようなので試してみた。

今回のコードは、GitHubのリポジトリに入れている。呼び出すAPIは、httpbinを利用させていただいた。

Kotlinのコード

gradle.kts

ktor関係のライブラリを追加する。また、シリアライザとして利用するGSONを追加した。sl4j-simpleを追加するとログが出せる。

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.4.10"
    application
}
group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib"))
    implementation("io.ktor:ktor-client-cio:1.4.0")
    implementation("io.ktor:ktor-client-apache:1.4.0")
    implementation("io.ktor:ktor-client-gson:1.4.0")
    implementation("io.ktor:ktor-client-logging-jvm:1.4.0")
    implementation("org.slf4j:slf4j-simple:1.7.26")
}

tasks.withType<KotlinCompile>() {
    kotlinOptions.jvmTarget = "11"
}
application {
    mainClassName = "MainKt"
}

Getメソッド(基本のパターン)

基本的な使い方。シリアライザでGSONを指定していると、APIの戻り値を自動でオブジェクトに変換してくれる。レスポンスはdata classで受け取ることができる。

fun callGetAPI() {
    runBlocking {
        val client = HttpClient(CIO) {
            install(Logging) {
                logger = Logger.DEFAULT
                level = LogLevel.ALL
            }
            install(JsonFeature) {
                serializer = GsonSerializer {
                    // .GsonBuilder
                    serializeNulls()
                    disableHtmlEscaping()
                    setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                }
            }
        }

        val message :HttpBinGetResponse = client.get("https://httpbin.org/get")

        println(message.args)
        println(message.headers["Host"])
        println(message.origin)
        println(message.url)

        client.close()
    }
}

data class HttpBinGetResponse(
    val args: Map<String, String>,
    val headers: Map<String, String>,
    val origin: String,
    val url: String)

Getメソッド(ヘッダーのカスタマイズ)

HTTP headerをカスタマイズするには・・?と思ったら、簡単に変えられた。

val message :HttpBinGetResponse = client.request {
    url("https://httpbin.org/get")
    method = HttpMethod.Get
    headers.append("User-Agent", "iPhone")
}

Getメソッド(ステータス確認)

普通、APIを呼び出したらHTTP Status Codeを確認すると思うんだけど・・。と思って、HTTP Status Codeを確認する方法を調べた。

val response = client.get<HttpResponse>("https://httpbin.org/get")

println(response.status)
val content = response.readText()
println(content)

val responseObj = Gson().fromJson(content, HttpBinGetResponse::class.java)
println(responseObj.headers["Host"])

でも、自分でデシリアライズするコードを書くのはめんどくさい・・。なので、例外をキャッチして例外処理する形で良さそう。

try {
    val message: HttpBinGetResponse = client.get("https://httpbin.org/status/500")

    println(message.args)
    println(message.headers["Host"])
    println(message.origin)
    println(message.url)
} catch (e :ServerResponseException) {
    println("status code:${e.response?.status?.value}")
}

Postメソッド

Bodyをdata classで渡せるし、レスポンスもdata classで受け取れる。

fun callPostAPI() {
    runBlocking {
        val client = HttpClient(CIO) {
            install(Logging) {
                logger = Logger.DEFAULT
                level = LogLevel.ALL
            }
            install(JsonFeature) {
                serializer = GsonSerializer {
                    // .GsonBuilder
                    serializeNulls()
                    disableHtmlEscaping()
                    setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                }
            }
        }

        val message = client.post<HttpBinPostResponse> {
            url("http://httpbin.org/post")
            contentType(ContentType.Application.Json)
            body = HttpBinPostBody(
                data1 = "dog",
                data2 = "cat")
        }

        println(message)
        println("data1:${message.json["data1"]}, data2:${message.json["data2"]}")

        client.close()
    }
}

data class HttpBinPostBody(val data1: String, val data2: String)
data class HttpBinPostResponse(
    val args: Map<String, String>,
    val headers: Map<String, String>,
    val origin: String,
    val data: String,
    val json: Map<String, String>,
    val url: String)

さいごに

外部APIを呼び出すことができたら、一気にKotlinアプリでできることが広がる。良い感じに使えるので、今後も使っていきたい。

Dev  Kotlin