使用 Kotlin 协程 + Fuel 调用 REST API

JSON : Placeholder

JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一个用于测试的 REST API 网站。
以下使用 RxJava2 + Retrofit2 调用该网站的 REST API,获取字符串以及 JSON 数据。

  • GET /posts/1
  • GET /posts
  • POST /posts
  • PUT /posts/1
  • DELETE /posts/1

所有 GET API 都返回JSON数据,格式(JSON-Schema)如下:

{
  "type":"object",
  "properties": {
    "userId": {"type" : "integer"},
    "id": {"type" : "integer"},
    "title": {"type" : "string"},
    "body": {"type" : "string"}
  }
}

创建工程

打开 Intellij IDEA,File / New / Project...
在 New Project 向导的第1页,选 Kotlin, Project Template 选 JVM Application,Build System 选 Gradle Kotlin,Project JDK 选 1.8。
在向导的第2页 Template 选 Console Application
点击 Finish 按钮创建工程

build.gradle.kts 内容如下

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

plugins {
    kotlin("jvm") version "1.4.32"
    application
}

group = "me.xxxx"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(kotlin("test-junit"))
}

tasks.test {
    useJUnit()
}

tasks.withType<KotlinCompile>() {
    kotlinOptions.jvmTarget = "1.8"
}

application {
    mainClassName = "MainKt"
}

将 dependencies 这部分的内容改为:

dependencies {
    testImplementation(kotlin("test-junit"))
    val fuel_version = "2.3.1"
    implementation("com.github.kittinunf.fuel:fuel:$fuel_version")
    implementation("com.github.kittinunf.fuel:fuel-coroutines:$fuel_version")
    implementation("com.github.kittinunf.fuel:fuel-gson:$fuel_version")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt")
}

这一段引用了 Kotlin 协程和 Fuel 这两个库。
其中 Fuel 这个库中还包含了 Gson 的转换器。

Post 对象

jsonschema2pojo 可以将 JSON 数据或格式自动转换为 Java 的 POJO 类。

data class Post(val userId: Int, val id: Int, val title: String, val body: String) {
    override fun toString() =
        "Post {userId = $userId, id = $id, title = \"$title\", body = \"${body.replace("\n", "\\n")}\"}"
}

Post 对象负责 Kotlin 对象与 JSON 数据之间的相互转换。
由于两者字段名相同,这里不需要使用注解。

// 如果需要加上注解的话
data class Post(@SerializedName("userId") val userId: Int,
                @SerializedName("id") val id: Int,
                @SerializedName("title") val title: String,
                @SerializedName("body") val body: String) {
// ...
}

Fuel 序列化

class PostDeserializer : ResponseDeserializable<Post> {
    override fun deserialize(content: String): Post =
        Gson().fromJson(content, Post::class.java)
}

class PostListDeserializer : ResponseDeserializable<List<Post>> {
    override fun deserialize(content: String): List<Post> {
        val listType = object : TypeToken<List<Post>>() {}.type
        return Gson().fromJson(content, listType)
    }
}

调用 REST API

private val home = "https://jsonplaceholder.typicode.com/"

suspend fun getPostAsString(): String =
    Fuel.get("${home}posts/1").awaitString()

suspend fun getPostAsJson(): Post =
    Fuel.get("${home}posts/1").awaitObject(PostDeserializer())

suspend fun getPosts(n: Int): List<Post> =
    Fuel.get("${home}posts").awaitObject(PostListDeserializer()).take(n)

suspend fun createPost(): Post =
    Fuel.post("${home}posts", listOf("userId" to 101, "title" to "test title", "body" to "test body"))
        .awaitObject(PostDeserializer())

suspend fun updatePost(): Post =
    Fuel.put("${home}posts/1", listOf("userId" to 101, "id" to 1, "title" to "test title", "body" to "test body"))
        .awaitObject(PostDeserializer())

suspend fun deletePost(): String =
    Fuel.delete("${home}posts/1").awaitString()
  • getPostAsString 函数取出第1个Post,返回字符串
  • getPostAsJson 函数取出第1个Post,返回Post对象
  • getPosts 函数取出前n个Post,返回n个Post对象
  • createPost 函数创建1个Post,返回所创建的Post对象
  • updatePost 函数更新第1个Post,返回所更新的Post对象
  • deletePost 函数删除第1个Post,返回字符串

main 函数

fun main(args: Array<String>) = runBlocking {
    println(getPostAsString())
    println(getPostAsJson())
    println(getPosts(2))
    println(createPost())
    println(updatePost())
    println(deletePost())
}

输出结果

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"}
[Post {userId = 1, id = 1, title = "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", body = "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"}, Post {userId = 1, id = 2, title = "qui est esse", body = "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"}]
Post {userId = 101, id = 101, title = "test title", body = "test body"}
Post {userId = 101, id = 1, title = "test title", body = "test body"}
{}
上一篇:emacs配置golang环境


下一篇:SpringBoot建立基于Markdown的博客—第1部分