改造 / OkHttp3 400 错误正文为空

问题描述 投票:0回答:5

我在我的 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中看到了上面两行的内容

android retrofit2 okhttp
5个回答
20
投票

我之前遇到过同样的问题,我使用代码 response.errorBody().string() only 修复了它一次。如果多次使用它,您将收到 IOException,因此建议将其用作一次性流,就像 ResponseBody 上的文档所述。

我的建议是:立即将 Stringified errorBody() 转换为对象,因为后者是您将在后续操作中使用的对象。


2
投票

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 而不消耗它。


0
投票

您可以使用 Gson 获取 errorBody 作为您想要的模型类:

val errorResponse: ErrorMessage? = Gson().fromJson(
                response.errorBody()!!.charStream(),
                object : TypeToken<ErrorMessage>() {}.type
            )

-1
投票

首先创建一个 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));
   }

希望这对您有帮助。祝你好运。


-3
投票

如果您收到 400,那么您尝试发送到服务器的请求是错误的。 检查您的获取要求。

© www.soinside.com 2019 - 2024. All rights reserved.