从 OKHttp 拦截器返回错误(使用改造)

2022-09-01 23:49:19

我正在使用 OkHttp 和 Retrofit 来发出我的应用的网络请求。我还使用拦截器进行身份验证,并在必要时重试请求。

服务器有时会出现临时问题,并返回空正文,尽管响应状态为 200 OK。这会导致我的应用崩溃,因为调用了改造回调的成功块,返回(并使用 GSON 进行分析)的自定义对象为 null,并且成功回调中的代码假定返回对象。

我已经向服务器团队报告了此问题,但我也想修复它,而不必使用空检查将所有成功回调代码包装到整个应用程序中。

目前,我倾向于两个选项,尽管任何其他想法都是最受欢迎的:1)不从拦截器返回(这甚至有可能吗?)并且只显示一个错误对话框2)返回一些将使Retrofit调用成为回调失败部分的内容。

我的代码在下面。如您所见,当收到空正文时,我最多重试请求 3 次。

@Override
public Response intercept(Chain chain) throws IOException
{
    // First
    Request request = chain.request();
    Response response = chain.proceed(request);

    ....
    ....
    ....

    // Retry empty body response requests for a maximum of 3 times
    Integer retryMaxCount = 3;
    MediaType contentType = response.body().contentType();
    String bodyString = response.body().string();

    while (bodyString.length() == 0 && retryMaxCount > 0)
    {
        //Empty body received!, Retrying...

        retryMaxCount--;
        response = chain.proceed(request);
        bodyString = response.body().string();
    }

    if (bodyString.length() != 0)
    {
        // Create and return new response because it was consumed
        ResponseBody newResponseBody = ResponseBody.create(contentType, bodyString);
        return response.newBuilder().body(newResponseBody).build();
    }
    else
    {
        // WHAT TO WRITE HERE???
    }
}

多谢。


答案 1

只是有相同的场景,这篇文章帮助我实施了解决方案。感谢@mastov指出正确的方向。

使用始终返回 HTTP 200 的后端 API,即使出现错误也是如此。这是我的错误响应示例

{"status":403,"message":"Bad User credentials","time":1495597740061,"version":"1.0"}

下面是一个简单的实现来补充这个答案。

public Response intercept(Chain chain) throws IOException {
        Request request   = chain.request();
        Response response = chain.proceed(request);
        ResponseBody body = response.body();
        // Only intercept JSON type responses and ignore the rest.
        if (body != null && body.contentType() != null && body.contentType().subtype() != null && body.contentType().subtype().toLowerCase().equals("json")) {
            String errorMessage = "";
            int errorCode       = 200; // Assume default OK
            try {
                BufferedSource source = body.source();
                source.request(Long.MAX_VALUE); // Buffer the entire body.
                Buffer buffer   = source.buffer();
                Charset charset = body.contentType().charset(Charset.forName("UTF-8"));
                // Clone the existing buffer is they can only read once so we still want to pass the original one to the chain.
                String json     = buffer.clone().readString(charset);
                JsonElement obj = new JsonParser().parse(json);
                // Capture error code an message.
                if (obj instanceof JsonObject && ((JsonObject) obj).has("status")) {
                    errorCode   = ((JsonObject) obj).get("status").getAsInt();
                }
                if (obj instanceof JsonObject && ((JsonObject) obj).has("message")) {
                    errorMessage= ((JsonObject) obj).get("message").getAsString();
                }
            } catch (Exception e) {
                Log.e(TAG, "Error: " + e.getMessage());
            }
            // Check if status has an error code then throw and exception so retrofit can trigger the onFailure callback method.
            // Anything above 400 is treated as a server error.
            if(errorCode > 399){
                throw new Exception("Server error code: " + errorCode + " with error message: " + errorMessage);
            }
        }

        return response;
    }

答案 2

你应该扔.在这种情况下,retrofit2 将使用路径。IOExceptiononFailure


推荐