Retrofit 2.0如何获得反序列化错误response.body
Posted
技术标签:
【中文标题】Retrofit 2.0如何获得反序列化错误response.body【英文标题】:Retrofit 2.0 how to get deserialised error response.body 【发布时间】:2015-12-07 18:54:20 【问题描述】:我正在使用 Retrofit 2.0.0-beta1。
在测试中,我有一个替代方案,预计会出现 HTTP 400 错误
我想要retrofit.Response<MyError> response
但是response.body() == null
MyError 没有反序列化 - 我只在这里看到它
response.errorBody().string()
但它没有给我 MyError 作为对象
【问题讨论】:
简单的futurestud.io/tutorials/retrofit-2-simple-error-handling 反序列化错误响应是一个好习惯吗?因为响应可能是 html 的网络服务器错误。 谢谢@ahmadalibaloch,这个链接真的很有帮助。 【参考方案1】:我目前使用一个非常简单的实现,它不需要使用转换器或特殊类。我使用的代码如下:
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response)
DialogHelper.dismiss();
if (response.isSuccessful())
// Do your success stuff...
else
try
JSONObject jObjError = new JSONObject(response.errorBody().string());
Toast.makeText(getContext(), jObjError.getJSONObject("error").getString("message"), Toast.LENGTH_LONG).show();
catch (Exception e)
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
这里要注意的一点是response.errorBody().string()
只会返回一次正确的值。如果再次调用它,它将返回一个空字符串。因此,如果您想重用它,请在第一次调用时将值存储在变量中。
【讨论】:
您的解决方案没有显示错误响应内容。 检查我的编辑,不知道为什么我一开始就这么不清楚。 最后,简单回答一个有效的 android 问题(大多数 android 答案都非常复杂)。 这绝对不是在回答问题。它只是返回错误消息而不是错误枚举对象。请关注此回复:***.com/a/21103420/2914140 在答案中它正在寻找到“消息”的映射,但我的错误响应没有。它有一个映射到“错误”。所以每个人都在阅读,这取决于你得到的回应!【参考方案2】:ErrorResponse 是您的自定义响应对象
科特林
val gson = Gson()
val type = object : TypeToken<ErrorResponse>() .type
var errorResponse: ErrorResponse? = gson.fromJson(response.errorBody()!!.charStream(), type)
Java
Gson gson = new Gson();
Type type = new TypeToken<ErrorResponse>() .getType();
ErrorResponse errorResponse = gson.fromJson(response.errorBody.charStream(),type);
【讨论】:
你不应该强制解包选项:gson.fromJson(response.errorBody()?.charStream(), type)
【参考方案3】:
我解决了这个问题:
if(!response.isSuccessful())
Gson gson = new Gson();
MyErrorMessage message=gson.fromJson(response.errorBody().charStream(),MyErrorMessage.class);
if(message.getCode()==ErrorCode.DUPLICATE_EMAIL_ID_CODE)
//DO Error Code specific handling
else
//DO GENERAL Error Code Specific handling
MyErrorMessage 类:
public class MyErrorMessage
private int code;
private String message;
public int getCode()
return code;
public void setCode(int code)
this.code = code;
public String getMessage()
return message;
public void setMessage(String message)
this.message = message;
【讨论】:
java.lang.IllegalStateException: 应为 BEGIN_OBJECT 但在第 1 行第 2 列路径 $ 使用.addConverterFactory(ScalarsConverterFactory.create())
@RonelGonzales【参考方案4】:
在 Retrofit 2.0 beta2 中,这是我收到错误响应的方式:
同步
try
Call<RegistrationResponse> call = backendServiceApi.register(data.in.account, data.in.password,
data.in.email);
Response<RegistrationResponse> response = call.execute();
if (response != null && !response.isSuccess() && response.errorBody() != null)
Converter<ResponseBody, BasicResponse> errorConverter =
MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
catch (IOException e)
//DO NETWORK ERROR HANDLING HERE
异步
Call<BasicResponse> call = service.loadRepo();
call.enqueue(new Callback<BasicResponse>()
@Override
public void onResponse(Response<BasicResponse> response, Retrofit retrofit)
if (response != null && !response.isSuccess() && response.errorBody() != null)
Converter<ResponseBody, BasicResponse> errorConverter =
retrofit.responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
@Override
public void onFailure(Throwable t)
//DO NETWORK ERROR HANDLING HERE
);
Retrofit 2 beta3 更新:
-
同步 - 未更改
异步 - Retrofit 参数已从 onResponse 中移除
Call<BasicResponse> call = service.loadRepo();
call.enqueue(new Callback<BasicResponse>()
@Override
public void onResponse(Response<BasicResponse> response)
if (response != null && !response.isSuccess() && response.errorBody() != null)
Converter<ResponseBody, BasicResponse> errorConverter =
MyApplication.getRestClient().getRetrofitInstance().responseConverter(BasicResponse.class, new Annotation[0]);
BasicResponse error = errorConverter.convert(response.errorBody());
//DO ERROR HANDLING HERE
return;
RegistrationResponse registrationResponse = response.body();
//DO SUCCESS HANDLING HERE
@Override
public void onFailure(Throwable t)
//DO NETWORK ERROR HANDLING HERE
);
【讨论】:
BasicResponse 中有什么? 只是一个包含消息和错误代码的基本 Jackson 注释类。在任何情况下,您都可以在那里拥有与您的服务器响应类型匹配的任何带注释的类。尝试使用jsonschema2pojo 生成一个符合您需求的文件。 对于其他人,您可以使用它来代替:ConverterList<BasicResponse>
怎么办?
你能帮我看看 MyApplication 类吗【参考方案5】:
其实很简单。
科特林:
val jsonObj = JSONObject(response.errorBody()!!.charStream().readText())
responseInterface.onFailure(jsonObj.getString("msg"))
Java:
if(response.errorBody()!=null)
JSONObject jsonObj = new JSONObject(TextStreamsKt.readText(response.errorBody().charStream()));
responseInterface.onFailure(jsonObj.getString("msg"));
else
responseInterface.onFailure("you might want to return a generic error message.");
在改造时测试:2.5.0。 从 charStream 中读取文本,这将为您提供一个字符串,然后解析为 JSONObject。
再见。
【讨论】:
java 上没有readText()
扩展,如果你还在使用 java,请使用 TextStreamsKt.readText(response.errorBody().charStream())
更新:改造 2.6.2 -> val errorMessage=jsonObj.getJSONArray("errors").getJSONObject(0).getString("message")
@Wale,为什么这对没有错误的 body 不起作用? JSONObject(response.body()!!.charStream().readText())
@Oleksandr 我不确定我是否收到您的问题,但我认为您的 response.body 没有 byteStream 函数,如果您使用 scala 转换器或相关或它,它可能以字符串形式出现如果您使用 google gson 转换器,可以简单地作为您传递的对象。所以,你想要做的可能是 this=> JSONObject (response.errorBody()!!.charStream().readText())
@TaslimOseni 我很高兴它也对你有用。【参考方案6】:
创建错误响应和用户 Gson 的模型以将响应转换为它。这样就可以了。
APIError.java
public class APIError
private String message;
public String getMessage()
return message;
MainActivity.java(在请求 onResponse 内)
if (response.isSuccessful())
// Do your success stuff...
else
APIError message = new Gson().fromJson(response.errorBody().charStream(), APIError.class);
Toast.makeText(MainActivity.this, "" + message.getMessage(), Toast.LENGTH_SHORT).show();
【讨论】:
这是迄今为止最优雅的方式。感谢您的回答...【参考方案7】: @Override
public void onResponse(Call<Void> call, retrofit2.Response<Void> response)
if (response.isSuccessful())
//Do something if response is ok
else
JsonParser parser = new JsonParser();
JsonElement mJson = null;
try
mJson = parser.parse(response.errorBody().string());
Gson gson = new Gson();
MyError errorResponse = gson.fromJson(mJson, MyError.class);
catch (IOException ex)
ex.printStackTrace();
【讨论】:
【参考方案8】:在 https://***.com/a/21103420/2914140 和 https://futurestud.io/tutorials/retrofit-2-simple-error-handling 中,此变体显示为 Retrofit 2.1.0。
call.enqueue(new Callback<MyResponse>()
@Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response)
if (response.isSuccessful())
...
else
Converter<ResponseBody, MyError> converter
= MyApplication.getRetrofit().responseBodyConverter(
MyError.class, new Annotation[0]);
MyError errorResponse = null;
try
errorResponse = converter.convert(response.errorBody());
catch (IOException e)
e.printStackTrace();
【讨论】:
【参考方案9】:如果您使用 Kotlin,另一种解决方案可能只是为 Response 类创建扩展函数:
inline fun <reified T>Response<*>.parseErrJsonResponse(): T?
val moshi = MyCustomMoshiBuilder().build()
val parser = moshi.adapter(T::class.java)
val response = errorBody()?.string()
if(response != null)
try
return parser.fromJson(response)
catch(e: JsonDataException)
e.printStackTrace()
return null
用法
val myError = response.parseErrJsonResponse<MyErrorResponse>()
if(myError != null)
// handle your error logic here
// ...
【讨论】:
终于有人用 Kotlin 的力量让代码易于阅读了!【参考方案10】:我使用 Retrofit 2.0-beta2 以这种方式进行异步调用:
@Override
public void onResponse(Response<RegistrationResponse> response,
Retrofit retrofit)
if (response.isSuccess())
// Do success handling here
else
try
MyError myError = (MyError)retrofit.responseConverter(
MyError.class, MyError.class.getAnnotations())
.convert(response.errorBody());
// Do error handling here
catch (IOException e)
e.printStackTrace();
【讨论】:
什么是 MyError 类? 我认为 onResponse 应该包含一个 Call 参数和一个 Response 参数。你的怎么有 Retrofit 参数? @MartyMiller 这是为以下版本的改造 Retrofit 2.0-beta2 完成的【参考方案11】:我遇到了同样的问题。我通过改造解决了它。让我展示一下……
如果你的错误 JSON 结构是这样的
"error":
"status": "The email field is required."
My ErrorRespnce.java
public class ErrorResponse
@SerializedName("error")
@Expose
private ErrorStatus error;
public ErrorStatus getError()
return error;
public void setError(ErrorStatus error)
this.error = error;
这是我的错误状态类
public class ErrorStatus
@SerializedName("status")
@Expose
private String status;
public String getStatus()
return status;
public void setStatus(String status)
this.status = status;
现在我们需要一个可以处理我们的 json 的类。
public class ErrorUtils
public static ErrorResponse parseError (Response<?> response)
Converter<ResponseBody , ErrorResponse> converter = ApiClient.getClient().responseBodyConverter(ErrorResponse.class , new Annotation[0]);
ErrorResponse errorResponse;
try
errorResponse = converter.convert(response.errorBody());
catch (IOException e)
return new ErrorResponse();
return errorResponse;
现在我们可以在改造 api 调用中检查我们的响应
private void registrationRequest(String name , String email , String password , String c_password)
final Call<RegistrationResponce> registrationResponceCall = apiInterface.getRegistration(name , email , password , c_password);
registrationResponceCall.enqueue(new Callback<RegistrationResponce>()
@Override
public void onResponse(Call<RegistrationResponce> call, Response<RegistrationResponce> response)
if (response.code() == 200)
else if (response.code() == 401)
ErrorResponse errorResponse = ErrorUtils.parseError(response);
Toast.makeText(MainActivity.this, ""+errorResponse.getError().getStatus(), Toast.LENGTH_SHORT).show();
@Override
public void onFailure(Call<RegistrationResponce> call, Throwable t)
);
就是这样,你可以展示你的 Toast
【讨论】:
应用程序崩溃 ErrorResponse$ErrorStatus.getStatus()' 在空对象引用上 它进入 ErrorUtils 的 catch 块,并说 End of input at line 1 column 1 path $【参考方案12】:这是使用 Kotlin
扩展的优雅解决方案:
data class ApiError(val code: Int, val message: String?)
companion object
val EMPTY_API_ERROR = ApiError(-1, null)
fun Throwable.getApiError(): ApiError?
if (this is HttpException)
try
val errorJsonString = this.response()?.errorBody()?.string()
return Gson().fromJson(errorJsonString, ApiError::class.java)
catch (exception: Exception)
// Ignore
return EMPTY_API_ERROR
及用法:
showError(retrofitThrowable.getApiError()?.message)
【讨论】:
喜欢这个。也可以轻松地进行通用化,以便您可以传入错误类型:fun <T:Exception> Throwable.getCustomException(classType: Class<T>): T?
,然后将 GSON 行更新为Gson().fromJson(errorJsonString, classType)
。用作e.getCustomException(CustomException::class.java)?
【参考方案13】:
if(!response.isSuccessful())
StringBuilder error = new StringBuilder();
try
BufferedReader bufferedReader = null;
if (response.errorBody() != null)
bufferedReader = new BufferedReader(new InputStreamReader(
response.errorBody().byteStream()));
String eLine = null;
while ((eLine = bufferedReader.readLine()) != null)
error.append(eLine);
bufferedReader.close();
catch (Exception e)
error.append(e.getMessage());
Log.e("Error", error.toString());
【讨论】:
这工作.. 使用 422 的 laravel api 响应 这是唯一适用于我的改造 2.9.0【参考方案14】:如果您只注入从 Retrofit 创建的服务,则不需要 Retrofit 实例。
public class ErrorUtils
public static APIError parseError(Context context, Response<?> response)
APIError error = new APIError();
try
Gson gson = new Gson();
error = gson.fromJson(response.errorBody().charStream(), APIError.class);
catch (Exception e)
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
if (TextUtils.isEmpty(error.getErrorMessage()))
error.setError(response.raw().message());
return error;
像这样使用它:
if (response.isSuccessful())
...
else
String msg = ErrorUtils.parseError(fragment.getActivity(), response).getError(); // would be from your error class
Snackbar.make(someview, msg, Snackbar.LENGTH_LONG).show();
【讨论】:
【参考方案15】:这似乎是你使用 OkHttp 和 Retrofit 时的问题,所以你可以删除 OkHttp 或使用下面的代码来获取错误正文:
if (!response.isSuccessful())
InputStream i = response.errorBody().byteStream();
BufferedReader r = new BufferedReader(new InputStreamReader(i));
StringBuilder errorResult = new StringBuilder();
String line;
try
while ((line = r.readLine()) != null)
errorResult.append(line).append('\n');
catch (IOException e)
e.printStackTrace();
【讨论】:
【参考方案16】:已经有很多有效的答案了。这只是一个用例的补充,当您需要多次使用相同的改造响应时。以下两个都不能使用,如you can read response body only once,因为它会在之后关闭,并且您每次尝试从同一个响应对象中读取时都会得到null
:
response()?.errorBody()?.charStream()?.readText()
response()?.errorBody()?.string()
相反,您可以获得响应字符串的只读副本(而响应本身可以传递并最终在以后使用):
response()?.errorBody()?.source()?.buffer?.snapshot()?.utf8()
【讨论】:
你救了我的命! THX【参考方案17】:经过测试并且可以工作
public BaseModel parse(Response<BaseModel> response , Retrofit retrofit)
BaseModel error = null;
Converter<ResponseBody, BaseModel> errorConverter =
retrofit.responseBodyConverter(BaseModel.class, new Annotation[0]);
try
if (response.errorBody() != null)
error = errorConverter.convert(response.errorBody());
catch (IOException e)
e.printStackTrace();
return error;
【讨论】:
【参考方案18】:json 响应
"success": false,
"status_code": 32,
"status_message": "Email not verified: Your email address has not been verified."
错误类
data class ResponseError(
@SerializedName("status_code")
val statusCode: Int,
@SerializedName("status_message")
val statusMessage: String,
@SerializedName("success")
val success: Boolean
)
获取错误信息
fun <T : Any> getResultOrError(response: Response<T>): T?
if (response.isSuccessful)
return response.body()
else
try
val responseError = Gson().fromJson(
response.errorBody()?.string(),
ResponseError::class.java
)
throw Throwable(responseError.statusMessage)
catch (e: Exception)
throw Throwable("Unknown error")
【讨论】:
【参考方案19】:解决了:
Converter<MyError> converter =
(Converter<MyError>)JacksonConverterFactory.create().get(MyError.class);
MyError myError = converter.fromBody(response.errorBody());
【讨论】:
如何通过 GsonConverterFactory 进行转换?有什么想法吗? 我找到了方法。我只是将JacksonConverterFactory
更改为 GsonConverterFactory
它会将 json 转换为我的自定义对象,但它会发出警告 Unchecked cast retrofit.Convertertry
ResponseBody response = ((HttpException) t).response().errorBody();
JSONObject json = new JSONObject( new String(response.bytes()) );
errMsg = json.getString("message");
catch(JSONException e)
return t.getMessage();
catch(IOException e)
return t.getMessage();
【讨论】:
【参考方案21】:在 Kotlin 中:
val call = APIClient.getInstance().signIn(AuthRequestWrapper(AuthRequest("1234567890z", "12341234", "nonce")))
call.enqueue(object : Callback<AuthResponse>
override fun onResponse(call: Call<AuthResponse>, response: Response<AuthResponse>)
if (response.isSuccessful)
else
val a = object : Annotation
val errorConverter = RentalGeekClient.getRetrofitInstance().responseBodyConverter<AuthFailureResponse>(AuthFailureResponse::class.java, arrayOf(a))
val authFailureResponse = errorConverter.convert(response.errorBody())
override fun onFailure(call: Call<AuthResponse>, t: Throwable)
)
【讨论】:
【参考方案22】:errorBody 值应该在 Retrofit 中设置 APIError 对象。因此,您可以使用以下代码结构。
public class APIErrorUtils
public static APIError parseError(Response<?> response)
Converter<ResponseBody, APIError> converter = API.getClient().responseBodyConverter(APIError.class, new Annotation[0]);
APIError error;
try
error = converter.convert(response.errorBody());
Log.d("SERVICELOG", "****************************************************");
Log.d("SERVICELOG", "***** SERVICE LOG");
Log.d("SERVICELOG", "***** TIMESTAMP: " + String.valueOf(error.getTimestamp()));
Log.d("SERVICELOG", "***** STATUS: " + String.valueOf(error.getStatus()));
Log.d("SERVICELOG", "***** ERROR: " + error.getError());
Log.d("SERVICELOG", "***** MESSAGE: " + error.getMessage());
Log.d("SERVICELOG", "***** PATH: " + error.getPath());
Log.d("SERVICELOG", "****************************************************");
catch (IOException e)
return new APIError();
return error;
APIError error = APIErrorUtils.parseError(response);
if (error.getStatus() == 400)
....
【讨论】:
【参考方案23】:val error = JSONObject(callApi.errorBody()?.string() as String)
CustomResult.OnError(CustomNotFoundError(userMessage = error["userMessage"] as String))
open class CustomError (
val traceId: String? = null,
val errorCode: String? = null,
val systemMessage: String? = null,
val userMessage: String? = null,
val cause: Throwable? = null
)
open class ErrorThrowable(
private val traceId: String? = null,
private val errorCode: String? = null,
private val systemMessage: String? = null,
private val userMessage: String? = null,
override val cause: Throwable? = null
) : Throwable(userMessage, cause)
fun toError(): CustomError = CustomError(traceId, errorCode, systemMessage, userMessage, cause)
class NetworkError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Usted no tiene conexión a internet, active los datos", cause)
class HttpError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage, cause)
class UnknownError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Unknown error", cause)
class CustomNotFoundError(traceId: String? = null, errorCode: String? = null, systemMessage: String? = null, userMessage: String? = null, cause: Throwable? = null):
CustomError(traceId, errorCode, systemMessage, userMessage?: "Data not found", cause)`
【讨论】:
【参考方案24】:kotlin Android 中的错误主体处理
catch (cause: Throwable)
when (cause)
is HttpException ->
try
val YourErrorResponseClassObj = Gson().fromJson(cause.response()?.errorBody()?.charStream(), YourErrorResponseClass::class.java)
catch (e: Exception)
else ->
//Other errors like Network ...
【讨论】:
【参考方案25】:非常简单。这救了我的命
public static void displayApiResponseErrorBody(Response<?> response)
InputStream i = response.errorBody().byteStream();
BufferedReader r = new BufferedReader(new InputStreamReader(i));
StringBuilder errorResult = new StringBuilder();
String line;
try
while ((line = r.readLine()) != null)
errorResult.append(line).append('\n');
Log.d("API_RESPONSE_ERROR_BODY",String.valueOf(errorResult));
System.out.println(errorResult);
catch (IOException e)
e.printStackTrace();
【讨论】:
【参考方案26】:如果您的错误响应是一个字符串,您可以使用以下 kotlin 代码对其进行反序列化:
val errorString = response.errorBody()?.byteStream()?.bufferedReader().use it?.readText() // defaults to UTF-8
【讨论】:
以上是关于Retrofit 2.0如何获得反序列化错误response.body的主要内容,如果未能解决你的问题,请参考以下文章
Retrofit 在 JSON 反序列化的时候提示 UnrecognizedPropertyException 异常