HTTP 504不满足请求(仅限缓存)

时间:2018-01-02 08:36:45

标签: android retrofit okhttp

Retrofit2.2.0和okhttp3.9.1脱机缓存无法正常工作,当我请求脱机数据时,将抛出异常HTTP 504不满足请求(仅限缓存)。数据正在从互联网上加载到设备中,但离线模式无法检索它。

演示: enter image description here

以下代码描述了API接口。

public class TestApi {


    private static TestService testService;

    private static final String TEST_URL = "https://httpbin.org/";


    public static TestService getTestService() {

        if(testService == null) {
            synchronized (TestApi.class) {
                if(testService == null) {
                    testService = XApi.getInstance().getRetrofit(TEST_URL, true).create(TestService.class);
                }
            }
        }
        return testService;
    }

}

以下代码描述了API服务。

public interface TestService {

    @GET("/cache/60")
    Flowable<TestBean> getTestDate();

}

以下代码描述了缓存控件拦截器。

public class CachingControlInterceptor {

    private static final int TIMEOUT_CONNECT = 60; //60 second
    private static final int TIMEOUT_DISCONNECT = 60 * 60 * 24 * 7; //7天

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            String cache = chain.request().header("cache");
            okhttp3.Response originalResponse = chain.proceed(chain.request());
            String cacheControl = originalResponse.header("Cache-Control");
            if (cacheControl == null) {
                if (cache == null || "".equals(cache)) {
                    cache = TIMEOUT_CONNECT + "";
                }
                originalResponse = originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, max-age=" + cache)
                    .build();
                return originalResponse;
            } else {
                return originalResponse;
            }
        }
    };

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (!NetworkUtils.isConnected()) {
                request = request.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached, max-stale="+TIMEOUT_DISCONNECT)
                    .build();
            }
            return chain.proceed(request);
        }
    };

}

最后,向OkHttpClient.Builder添加一个拦截器

Retrofit.Builder builder = new Retrofit.Builder()
    .baseUrl(baseUrl)
    .client(getClient(baseUrl, provider))
    .addConverterFactory(GsonConverterFactory.create());

builder.addNetworkInterceptor(CachingControlInterceptor.REWRITE_RESPONSE_INTERCEPTOR);
builder.addInterceptor(CachingControlInterceptor.REWRITE_RESPONSE_INTERCEPTOR_OFFLINE);

而且我不知道如何解决它。

希望有人能帮助我!

2 个答案:

答案 0 :(得分:1)

我仍然不明白你究竟想要做什么,但从一个更简单的可执行例子开始,以下是什么问题?

package com.baulsupp.oksocial;

import okhttp3.*;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import retrofit2.http.GET;

import java.io.File;
import java.io.IOException;

public class TestRequest {
    private static boolean connected = true;

    public interface TestService {
        @GET("/cache/60")
        Call<String> getTestDate();
    }

    public static final Interceptor REWRITE_RESPONSE_INTERCEPTOR_OFFLINE = new Interceptor() {
        @Override
        public okhttp3.Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (isConnected()) {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
            } else {
                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
            }
            Response response = chain.proceed(request);

            System.out.println("network: " + response.networkResponse());
            System.out.println("cache: " + response.cacheResponse());

            return response;
        }
    };

    private static boolean isConnected() {
        return connected;
    }

    public static void main(String[] args) throws IOException {

        OkHttpClient.Builder clientBuilder =
                new OkHttpClient.Builder().cache(new Cache(new File("/tmp/http"), 10 * 1024 * 1024));

        clientBuilder.addInterceptor(REWRITE_RESPONSE_INTERCEPTOR_OFFLINE);

        Retrofit builder = new Retrofit.Builder()
                .addConverterFactory(ScalarsConverterFactory.create())
                .baseUrl("https://httpbin.org/")
                .client(clientBuilder.build())
                .build();

        TestService service = builder.create(TestService.class);

        connected = true;

        String online = service.getTestDate().execute().body();
        System.out.println(online);

        connected = false;

        String offline = service.getTestDate().execute().body();
        System.out.println(offline);
    }
}

输出

network: Response{protocol=http/1.1, code=200, message=OK, url=https://httpbin.org/cache/60}
cache: null
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Cache-Control": "no-cache", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "okhttp/3.9.1"
  }, 
  "origin": "82.5.95.16", 
  "url": "https://httpbin.org/cache/60"
}

network: null
cache: Response{protocol=http/1.1, code=200, message=OK, url=https://httpbin.org/cache/60}
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Cache-Control": "no-cache", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "okhttp/3.9.1"
  }, 
  "origin": "82.5.95.16", 
  "url": "https://httpbin.org/cache/60"
}

答案 1 :(得分:0)

only-if-cached: 由客户端设置以指示“不使用网络”作为响应。缓存应该使用存储的响应进行响应,或者使用 504 状态代码进行响应。不应设置条件标头,例如 If-None-Match。如果服务器将 only-if-cached 设置为响应的一部分,则没有效果。 (参考:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cache_request_directives

因此,“only-if-cached”是一个缓存请求指令,不应在响应头中使用。但是您可以尝试将 ("Cache-Control", "max-stale, only-if-cached") 添加到请求标头以允许仅缓存过时响应。