Skip to content

ktor for http request

在 KMP (Kotlin Multiplatform) 开发中,处理 HTTP 请求(常用 Ktor 库,非 "kor")、JSON 数据解析RESTful API 交互 是跨平台网络层的核心能力。以下从工具选型、核心实现、平台适配到最佳实践,系统梳理完整方案。

一、核心工具选型(跨平台友好)

KMP 网络层需兼顾 多平台一致性(Android/iOS/Desktop/Web)和 性能,推荐组合如下:

功能场景推荐库核心优势
HTTP 请求Ktor ClientKMP 官方推荐,原生支持多平台,轻量且可灵活配置(引擎、拦截器等)
JSON 数据解析Kotlinx SerializationKotlin 官方序列化库,无反射(避免 iOS 反射限制),支持多平台和数据类适配
RESTful API 辅助Ktor Client + 自定义封装结合 Ktor 的请求构建能力,封装 REST 标准方法(GET/POST/PUT/DELETE)

二、Step 1:环境配置(build.gradle.kts)

首先在 KMP 项目的 共享模块(shared) 中添加依赖,确保多平台兼容性。

1. 基础配置(共享模块 build.gradle.kts)

kotlin
plugins {
    kotlin("multiplatform") version "1.9.20"
    kotlin("plugin.serialization") version "1.9.20" // JSON 序列化插件
    id("io.ktor.plugin") version "2.3.7" // Ktor 插件(可选,简化配置)
}

kotlin {
    // 声明目标平台(根据需求调整)
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    jvm("desktop")
    
    sourceSets {
        // 所有平台共享的代码
        val commonMain by getting {
            dependencies {
                // 1. Ktor Client 核心(多平台)
                implementation("io.ktor:ktor-client-core:2.3.7")
                // 2. Ktor Client 序列化(JSON 解析集成)
                implementation("io.ktor:ktor-client-serialization:2.3.7")
                implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
                // 3. Kotlinx Serialization(JSON 核心)
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
                // 4. 可选:日志拦截器(调试用)
                implementation("io.ktor:ktor-client-logging:2.3.7")
            }
        }

        // 平台特定引擎(HTTP 底层实现,需单独配置)
        val androidMain by getting {
            dependencies {
                // Android 平台:使用 OkHttp 引擎(稳定、支持 HTTP/2)
                implementation("io.ktor:ktor-client-okhttp:2.3.7")
            }
        }

        val iosMain by getting {
            dependencies {
                // iOS 平台:使用 Darwin 引擎(基于 iOS 原生网络库)
                implementation("io.ktor:ktor-client-darwin:2.3.7")
            }
        }

        val desktopMain by getting {
            dependencies {
                // Desktop 平台:使用 Java 引擎(基于 JDK HttpURLConnection)
                implementation("io.ktor:ktor-client-java:2.3.7")
            }
        }
    }
}

三、Step 2:JSON 数据结构定义(Kotlinx Serialization)

使用 @Serializable 注解标记数据类,Kotlinx Serialization 会自动生成序列化/反序列化代码(无反射)。

示例:用户数据模型

kotlin
// shared/src/commonMain/kotlin/com/your/package/model/User.kt
import kotlinx.serialization.Serializable

// 1. 基础数据类(与 REST API 响应字段一一对应)
@Serializable
data class User(
    val id: String,          // 对应 API 中的 "id" 字段
    val name: String,        // 对应 API 中的 "name" 字段
    @SerialName("avatar_url") // 若字段名不一致,用 @SerialName 映射
    val avatarUrl: String,
    val email: String? = null // 可选字段(API 可能返回 null)
)

// 2. 列表响应模型(API 常返回 "data: []" 结构)
@Serializable
data class UserListResponse(
    val code: Int,           // 状态码(如 200=成功)
    val message: String,     // 提示信息(如 "success")
    val data: List<User>     // 核心数据列表
)

// 3. 错误响应模型(统一错误格式)
@Serializable
data class ApiError(
    val code: Int,           // 错误码(如 400=参数错误)
    val message: String      // 错误信息(如 "用户名已存在")
)

四、Step 3:RESTful API 网络层封装(Ktor Client)

基于 Ktor Client 封装通用网络工具类,统一处理 请求配置、拦截器、异常捕获、JSON 解析,避免重复代码。

1. 网络客户端单例(多平台通用)

kotlin
// shared/src/commonMain/kotlin/com/your/package/network/ApiClient.kt
import io.ktor.client.HttpClient
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logging
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json

// 网络客户端单例(需传入平台特定引擎,如 Android 的 OkHttp、iOS 的 Darwin)
class ApiClient(engine: HttpClientEngine) {
    val client = HttpClient(engine) {
        // 1. 日志配置(调试用,生产环境可关闭)
        install(Logging) {
            level = LogLevel.ALL // 打印请求/响应详情
        }

        // 2. JSON 序列化配置(集成 Kotlinx Serialization)
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true // 忽略 API 响应中未定义的字段(避免解析崩溃)
                isLenient = true         // 宽松解析(如允许 JSON 中的注释、换行)
                encodeDefaults = true    // 序列化时包含默认值(如 null 或空列表)
            })
        }

        // 3. 通用请求配置(可选)
        defaultRequest {
            url("https://api.your-domain.com/v1/") // 基础 URL(REST API 前缀)
            headers {
                // 添加通用请求头(如 Token、Content-Type)
                append("Content-Type", "application/json")
                append("Authorization", "Bearer {your_token}") // 后续可动态替换 Token
            }
        }
    }
}

2. RESTful API 方法封装(GET/POST/PUT/DELETE)

基于 ApiClient 封装具体的 REST 接口,返回 Result<T> 类型(Kotlin 标准库,统一处理成功/失败)。

kotlin
// shared/src/commonMain/kotlin/com/your/package/network/ApiService.kt
import com.your.package.model.ApiError
import com.your.package.model.User
import com.your.package.model.UserListResponse
import io.ktor.client.call.body
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.put
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType

class ApiService(private val apiClient: ApiClient) {
    // 1. GET 请求:获取用户列表(带查询参数)
    suspend fun getUserList(page: Int, pageSize: Int = 20): Result<UserListResponse> {
        return try {
            val response = apiClient.client.get("users") {
                // 添加查询参数(URL 后拼接 ?page=1&pageSize=20)
                url.parameters.append("page", page.toString())
                url.parameters.append("pageSize", pageSize.toString())
            }
            Result.success(response.body())
        } catch (e: Exception) {
            Result.failure(handleApiError(e))
        }
    }

    // 2. GET 请求:获取单个用户(路径参数)
    suspend fun getUserById(userId: String): Result<User> {
        return try {
            val response = apiClient.client.get("users/$userId") // 路径参数:users/123
            Result.success(response.body())
        } catch (e: Exception) {
            Result.failure(handleApiError(e))
        }
    }

    // 3. POST 请求:创建用户(请求体为 JSON)
    suspend fun createUser(user: User): Result<User> {
        return try {
            val response = apiClient.client.post("users") {
                contentType(ContentType.Application.Json) // 明确 Content-Type
                setBody(user) // 自动序列化为 JSON(基于 Kotlinx Serialization)
            }
            Result.success(response.body())
        } catch (e: Exception) {
            Result.failure(handleApiError(e))
        }
    }

    // 4. PUT 请求:更新用户
    suspend fun updateUser(userId: String, user: User): Result<User> {
        return try {
            val response = apiClient.client.put("users/$userId") {
                contentType(ContentType.Application.Json)
                setBody(user)
            }
            Result.success(response.body())
        } catch (e: Exception) {
            Result.failure(handleApiError(e))
        }
    }

    // 5. DELETE 请求:删除用户
    suspend fun deleteUser(userId: String): Result<Unit> {
        return try {
            apiClient.client.delete("users/$userId")
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(handleApiError(e))
        }
    }

    // 辅助:统一处理 API 错误(解析错误响应或网络异常)
    private fun handleApiError(e: Exception): Exception {
        // 1. 若为 Ktor 响应错误(如 400/500),尝试解析 API 错误信息
        if (e is io.ktor.client.plugins.ResponseException) {
            return try {
                val errorBody = e.response.body<ApiError>()
                Exception("Error ${errorBody.code}: ${errorBody.message}")
            } catch (innerE: Exception) {
                // 解析失败,返回原始错误
                Exception("API Error: ${e.response.status}")
            }
        }
        // 2. 网络异常(如无网、超时)
        return Exception("Network Error: ${e.message ?: "Unknown error"}")
    }
}

五、最佳实践与注意事项

  1. Token 动态刷新
    在 Ktor Client 中添加 Auth 拦截器,监听 401 错误(Token 过期),自动刷新 Token 后重试请求。

  2. 网络状态监听
    各平台需单独实现网络状态检测(如 Android 使用 ConnectivityManager,iOS 使用 NWPathMonitor),避免无网时发起无效请求。

  3. iOS 反射限制
    严禁使用 Java 反射(如 Gson 库),必须使用 Kotlinx Serialization(编译期生成代码,无反射),否则 iOS 端会崩溃。

  4. 线程安全
    KMP 协程在各平台自动适配线程(Android 用 Dispatchers.Main,iOS 用 KotlinxCoroutinesDispatcher),避免手动切换线程。

  5. 测试
    使用 Ktor Client 的 MockEngine 模拟 API 响应,在共享模块中编写跨平台网络测试(无需依赖真实后端)。

通过以上方案,可实现 KMP 跨平台项目中 RESTful API 交互、JSON 解析、HTTP 请求 的统一封装,兼顾一致性和平台适配性。