Retrofit自定义错误处理器实现及errorBody反序列化问题咨询
Retrofit 错误体自动反序列化实战
问题背景
我是Retrofit新手,想要实现自动反序列化result.errorBody()中的错误信息,已经尝试两种方式但均失败,求正确实现方法。
现有代码
自定义错误转换器(CustomErrorHandler)
class CustomErrorHandler(private val gson: Gson) : Converter<ResponseBody, Error> { @Throws(IOException::class) override fun convert(responseBody: ResponseBody): Error { val error = gson.fromJson(responseBody.charStream(), Error::class.java) responseBody.close() throw Exception(error.message) } class Factory(private val gson: Gson) : Converter.Factory() { override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? { val typeToken = TypeToken.get(type) if (typeToken.rawType != Error::class.java) { return null } return CustomErrorHandler(gson) } } }
Retrofit 初始化代码
val retrofit = Retrofit.Builder().baseUrl("http://192.168.0.1:8080") .addConverterFactory(CustomErrorHandler.Factory(GsonBuilder().create())) .addConverterFactory(GsonConverterFactory.create()).build()
Error 数据类
data class Error(val code: String, val message: String) { override fun toString(): String { return "code: $code, message: $message" } }
RoleService 接口
interface RoleService { @GET("/roles") @Headers("Accept: application/json") suspend fun findAll(): Response<List<Role>> }
尝试1:期望自动转换errorBody
suspend fun findAll(): List<Role>? { val result = roleService.findAll() if (result.isSuccessful) return result.body() throw Exception(result.errorBody().toString()) // 无法将errorBody转为Error类 }
尝试2:手动解析errorBody(失败)
suspend fun findAll(): List<Role>? { val result = roleService.findAll() if (result.isSuccessful) return result.body() val e = Gson().fromJson(result.errorBody().toString(), Error::class.java) // 解析失败 throw Exception(e.message) }
错误原因分析
- 尝试1问题:
result.errorBody().toString()返回的是ResponseBody对象的字符串标识(如okhttp3.ResponseBody$1@xxxx),并非接口返回的JSON错误内容;且自定义转换器仅处理成功响应的Body,不会自动解析错误体。 - 尝试2问题:同样误用
errorBody().toString()获取内容,应读取ResponseBody的实际JSON流;且重复创建Gson实例既不高效,也可能与Retrofit配置的Gson实例不一致。
正确实现方案
- 方案1:复用自定义转换器解析错误体
已定义的CustomErrorHandler可直接从Retrofit中获取,用于解析错误体:
// 从Retrofit中获取自定义错误转换器 val errorConverter = retrofit.responseBodyConverter<Error>(Error::class.java, emptyArray()) suspend fun findAll(): List<Role>? { val result = roleService.findAll() if (result.isSuccessful) { return result.body() } result.errorBody()?.let { body -> val error = try { errorConverter.convert(body) } catch (e: IOException) { Error("UNKNOWN", "解析错误失败") } finally { body.close() } throw Exception(error.message) } ?: throw Exception("未知错误") }
- 方案2:手动解析错误体(优化版)
无需转换器,直接读取错误体JSON内容解析,注意复用Retrofit配置的Gson实例:
// 复用全局Gson实例,避免重复创建 val gson = GsonBuilder().create() suspend fun findAll(): List<Role>? { val result = roleService.findAll() if (result.isSuccessful) { return result.body() } result.errorBody()?.use { body -> // 读取错误体的JSON字符串(string()仅能调用一次) val errorJson = body.string() val error = gson.fromJson(errorJson, Error::class.java) throw Exception(error.message) } ?: throw Exception("未知错误") }
注意:
ResponseBody.string()会关闭流,因此只能调用一次,使用use块可自动关闭资源。
- 方案3:全局错误处理(更优雅)
自定义CallAdapter实现全局错误拦截,无需每个接口重复编写解析逻辑:
class ErrorHandlingCallAdapterFactory(private val gson: Gson) : CallAdapter.Factory() { override fun get( returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit ): CallAdapter<*, *>? { val delegate = retrofit.nextCallAdapter(this, returnType, annotations) return object : CallAdapter<Any, Any> { override fun responseType(): Type = delegate.responseType() override fun adapt(call: Call<Any>): Any { return delegate.adapt(call).also { response -> if (response is Response<*> && !response.isSuccessful) { response.errorBody()?.use { body -> val error = gson.fromJson(body.charStream(), Error::class.java) throw Exception(error.message) } ?: throw Exception("未知错误") } } } } } }
在Retrofit初始化时添加该CallAdapter:
val gson = GsonBuilder().create() val retrofit = Retrofit.Builder().baseUrl("http://192.168.0.1:8080") .addCallAdapterFactory(ErrorHandlingCallAdapterFactory(gson)) .addConverterFactory(GsonConverterFactory.create(gson)) .build()
此时接口可直接返回业务数据类型,错误会自动抛出:
interface RoleService { @GET("/roles") @Headers("Accept: application/json") suspend fun findAll(): List<Role> } // 调用时无需处理Response,直接捕获异常即可 suspend fun findAll(): List<Role> { return roleService.findAll() }
内容的提问来源于stack exchange,提问作者Source
相关产品推荐
相关产品推荐

