我在我的 Android 项目中使用 Retrofit 2。当我使用 GET 方法访问 API 端点并返回 400 级别错误时,我可以在使用 HttpLoggingInterceptor 时看到错误内容,但当我到达 Retrofit OnResponse 回调时,错误正文的字符串为空。
我可以看到错误有一个主体,但在 Retrofit 回调的上下文中我似乎无法拉出该主体。有没有办法确保尸体在那里可以接触到?
谢谢, 亚当
编辑: 我从服务器看到的响应是: {"error":{"errorMessage":"对于输入字符串:\"000001280_713870281\"","httpStatus":400}}
我试图通过以下方式从响应中提取该响应:
BaseResponse baseResponse = GsonHelper.getObject(BaseResponse.class, response.errorBody().string());
if (baseResponse != null && !TextUtils.isEmpty(baseResponse.getErrorMessage()))
error = baseResponse.getErrorMessage();
(GsonHelper只是一个助手,它通过GSON传递JSON字符串来拉取
BaseResponse
类型的对象)
对response.errorBody().string()的调用会导致IOException:Content-Length和流长度不一致,但我在Log Cat中看到了上面两行的内容
我之前遇到过同样的问题,我使用代码 response.errorBody().string() only 修复了它一次。如果多次使用它,您将收到 IOException,因此建议将其用作一次性流,就像 ResponseBody 上的文档所述。
我的建议是:立即将 Stringified errorBody() 转换为对象,因为后者是您将在后续操作中使用的对象。
TL;DR 使用下面的代码从
response
获取错误正文字符串。可以重复使用,第一次使用后不会返回空字符串。
public static String getErrorBodyString(Response<?> response) throws IOException {
// Get a copy of error body's BufferedSource.
BufferedSource peekSource = response.errorBody().source().peek();
// Get the Charset, as in the original response().errorBody().string() implementation
MediaType contentType = response.errorBody().contentType(); //
Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
charset = Util.bomAwareCharset(peekSource, charset);
// Read string without consuming data from the original BufferedSource.
return peekSource.readString(charset);
}
说明: 正如前面提到的,您只需使用
response.errorBody().string()
一次。但是有一种方法可以多次获取错误主体字符串。
这是基于原始的 response.errorBody().string()
方法实现。它使用 BufferedSource
中 peek()
的副本并返回错误正文字符串而不消耗它,因此您可以根据需要多次调用它。
如果您查看
response.errorBody().string()
方法实现,您会看到以下内容:
public final String string() throws IOException {
try (BufferedSource source = source()) {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
}
}
source.readString(charset)
消耗错误主体 BufferedSource
实例的数据,这就是为什么 response.errorBody().string()
在下次调用时返回空字符串。
要从错误主体的
BufferedSource
中读取而不消耗它,我们可以使用 peek()
,它基本上返回原始 BufferedSource
的副本:
返回一个新的BufferedSource,可以从中读取数据 BufferedSource 而不消耗它。
您可以使用 Gson 获取 errorBody 作为您想要的模型类:
val errorResponse: ErrorMessage? = Gson().fromJson(
response.errorBody()!!.charStream(),
object : TypeToken<ErrorMessage>() {}.type
)
首先创建一个 Error 类,如下所示:
public class ApiError {
@SerializedName("httpStatus")
private int statusCode;
@SerializedName("errorMessage")
private String message;
public ApiError() {
}
public ApiError(String message) {
this.message = message;
}
public ApiError(int statusCode, String message) {
this.statusCode = statusCode;
this.message = message;
}
public int status() {
return statusCode;
}
public String message() {
return message;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
}
其次,您可以创建一个 Utils 类来处理您的错误,如下所示:
public final class ErrorUtils {
private ErrorUtils() {
}
public static ApiError parseApiError(Response<?> response) {
final Converter<ResponseBody, ApiError> converter =
YourApiProvider.getInstance().getRetrofit()
.responseBodyConverter(ApiError.class, new Annotation[0]);
ApiError error;
try {
error = converter.convert(response.errorBody());
} catch (IOException e) {
error = new ApiError(0, "Unknown error"
}
return error;
}
最后像下面这样处理你的错误:
if (response.isSuccessful()) {
// Your response is successfull
callback.onSuccess();
}
else {
callback.onFail(ErrorUtils.parseApiError(response));
}
希望这对您有帮助。祝你好运。
如果您收到 400,那么您尝试发送到服务器的请求是错误的。 检查您的获取要求。