如何处理Ktor ContentNegotiation遇到意外响应时的序列化错误?
处理Ktor ContentNegotiation中的意外错误响应
在使用Ktor的ContentNegotiation做JSON序列化时,遇到服务端返回非预期的错误负载(比如含error字段的结构)导致JsonConvertException,可以通过以下几种方式解决:
方法一:根据HTTP状态码条件解析响应
服务端返回错误时通常会伴随非2xx的状态码,先获取完整响应对象,根据状态码选择对应的序列化类型,从根源避免触发序列化异常:
- 定义正常响应和错误响应的数据类:
@Serializable data class NormalResponse(val data: YourDataModel) @Serializable data class ErrorResponse(val error: ErrorInfo) @Serializable data class ErrorInfo(val code: Int, val message: String)
- 发起请求时先获取
HttpResponse对象,再根据状态码分支解析:
val client = HttpClient(OkHttp) { install(ContentNegotiation) { Json { ignoreUnknownKeys = true // 忽略响应中的未知字段,提升兼容性 isLenient = true } } } val response = client.post("https://apis.google.com/some-service") { // 配置请求体、Headers等参数 } when { response.status.isSuccess() -> { val normalData = response.body<NormalResponse>() // 处理正常业务逻辑 } else -> { val errorData = response.body<ErrorResponse>() // 处理错误逻辑,比如展示error.message } }
方法二:捕获序列化异常后手动解析错误
如果服务端错误响应仍返回2xx状态码(这种场景较少见),可以捕获JsonConvertException,读取原始响应内容后手动解析为错误结构:
try { val normalData = client.post("https://apis.google.com/some-service") { // 请求配置 }.body<NormalResponse>() // 处理正常响应 } catch (e: JsonConvertException) { // 读取原始响应文本 val rawResponse = client.post("https://apis.google.com/some-service") { // 复用之前的请求配置 }.body<String>() // 手动解析为错误响应 val errorData = Json.decodeFromString<ErrorResponse>(rawResponse) // 处理错误逻辑 }
提示:如果不想重复发起请求,可以改用
client.execute获取HttpCall对象,先尝试解析正常响应,失败后直接读取call.response.content()的内容,避免二次请求。
额外优化:用密封类统一响应类型
可以定义密封类封装正常和错误响应,让代码更简洁易维护:
sealed class ApiResponse<out T> { data class Success<out T>(val data: T) : ApiResponse<T>() data class Error(val errorInfo: ErrorInfo) : ApiResponse<Nothing>() } // 扩展函数简化解析流程 suspend fun <T> HttpResponse.parseResponse(): ApiResponse<T> where T : @Serializable { return try { if (status.isSuccess()) { ApiResponse.Success(body<T>()) } else { ApiResponse.Error(body<ErrorResponse>().error) } } catch (e: JsonConvertException) { val errorRaw = body<String>() val errorResp = Json.decodeFromString<ErrorResponse>(errorRaw) ApiResponse.Error(errorResp.error) } }
使用时只需:
val response = client.post("https://apis.google.com/some-service") { // 请求配置 }.parseResponse<NormalResponse>() when (response) { is ApiResponse.Success -> { // 处理response.data } is ApiResponse.Error -> { // 处理response.errorInfo } }
内容的提问来源于stack exchange,提问作者shadyyx
相关产品推荐
相关产品推荐

