Analyzing some previously written code I have some questions concerning the set up with OkHttpClient. We did create a single OkHttpClient instance and reuse it for all of our HTTP calls. We execute REST API calls and caching is not needed.
However I do see some code in an interceptor
request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=${120}").build()
Question 1: Would this have any effect if caching is not enabled?
Secondly there is one API call that fetches configuration data and I feel it can benefit from caching.
Question 2: Can we enable caching for just one call, say, if we customize the client using newBuilder()?
It could have an effect on caches between yourself and the remote service. But this specific interceptor seems designed to force the request to only use the local cache, and not attempt to use the network.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache-control/only-if-cached/
To rephrase my question slightly ...if no cache is set on the original call then the request with the cache-control will not do anything right?
Typically you would not need an interceptor, instead you would apply the cache settings to the request.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-cache/
Request request = new Request.Builder()
.cacheControl(new CacheControl.Builder().noCache().build())
.url("http://publicobject.com/helloworld.txt")
.build();
Not sure this addresses the second question. I'm asking if we have set up one single OkHttpClient instance can we have only some requests cached or the fact that we turn on cache for one request turns it on for all other requests onward?
Related
Currently, we only have one okhttpclient in our app, by some of our request need to intercept the original reponse and some want to follow the redirect.
we can identify the request whether it needs to redirect, but a okhttp client only has a global param to control this, how can we customize for each request?
You could use a network Interceptor to remove the server response’s Location header. If you need that data you can put it into another header like Rewritten-Location. Either way if the location header is absent the response will not be redirected.
I have a simple use case where a server returns an Etag for a request and that etag is added as a header (i.e. If-None-Match) to all subsequent url requests. The server can respond with a 200 if there is a change in the response or a 304 otherwise. For the latter, it makes sense to reuse the response from cache. But okhttp always returns null as the cached response.
I did some troubleshooting and the response is written to disk internally by okhttp but the CachingStrategy doesn't return it to the CacheInterceptor. Looking closely into the CachingStrategy class, there is documentation specifically stating that the cache won't be used:
/**
* Returns true if the request contains conditions that save the server from sending a response
* that the client has locally. When a request is enqueued with its own conditions, the built-in
* response cache won't be used.
*/
private fun hasConditions(request: Request): Boolean =
request.header("If-Modified-Since") != null || request.header("If-None-Match") != null
}
Is there a way to workaround this behavior and use the cached response instead?
(I have caching enabled as described here and I am using okhttp version 3.10.0)
Also, strangely I am having to manually add the Etag. Okhttp fails to add the etag to subsequent requests on its own.
Edit - correction, Okhttp correctly adds the etag header. I needed to use a network interceptor vs a regular one to see the etag header being added to the subsequent requests
Somewhat related to these issues:
https://github.com/square/okhttp/issues/1400
https://github.com/square/okhttp/issues/831#issuecomment-57900979
Either you do your own caching, or you let OkHttp’s cache do the caching. When you add your own conditions, OkHttp assumes you want full control of the caching and it does not participate in that.
Is it possible to add an Interceptor to an okHttp3 client that is already built? I use a singleton client and want to add an Interceptor to an already built client so I can add a custom cookie to the client without having to manually add it to each request.
Accessing the interceptors list directly from the client returns an immutable list.
This is what the newBuilder() method is designed for:
val modifiedClient = client.newBuilder()
.addNetworkInterceptor(MyCookieInterceptor())
.build()
You can customize a shared OkHttpClient instance with newBuilder(). This builds a client that shares the same connection pool, thread pools, and configuration. Use the builder methods to configure the derived client for a specific purpose.
See the first couple paragraphs of the javadoc for more details.
I am building my OkHttp in the application class and holding a static reference to it. This way, I have one instance of OkHttp at any given time.
okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(context.getCacheDir(), 50 * 1024 * 1024))
.addInterceptor(new HttpInterceptor())
.build();
Now, I am making two kind of requests, one requires OAuth2 while the other require the API key and secret supplied as parameters to the request URL. I am using an interceptor for the request that requires an OAuth2.
So the problem is the latter request uses resorts to the interceptor for authentication, thus the request fails. Is there any way I can tell it "See, ignore the interceptor for this request"? Or do I need two instances of OkHttpClient?
You will need two instances since after creating single instance wit interceptor attached to it will not ignore interceptor.
A hack can be to identify the url in the interceptor and ignore interceptor operation for that request.
The cleanest solution is to have a single OkHttp instance, but in your interceptor have the logic to decide how to authenticate.
This example ServiceInterceptor.java checks each request and decides how to authenticate in a pluggable manner. You could do something similar.
There are many benefits of this approach over different clients including
reuse of the executor
possibility that the connections can be coalesced for multiple hosts e.g. www.mysvc.com and api.mysvc.com
Support for redirection across hosts
I'm working on an Android App, where i'm using Retrofit to communicate with a rest API.
This API provides a login, to get X Auth Tokens.
I have an interceptor to set this tokens for every request like this:
#Override
public void intercept(RequestFacade request) {
request.addHeader("X-Auth-SOFTTOKEN", softToken);
request.addHeader("X-Auth-HARDTOKEN", hardToken);
request.addHeader("X-Auth-USER", username);
}
I can observe a strange behaviour, where i get Cached responses for the old User, although the X Auth Tokens are different and i should get the response for the new logged in user.
If i add this piece of code to my rest API interface methods, it works.
#Headers("Cache-Control: no-cache")
I can't find any hints if this is expected behaviour or not. I thought the cache should only beeing triggered, if the request is exactly the same.
Have a look at this answer: https://stackoverflow.com/a/35993722/3964585 and from there also http RFC - https://www.rfc-editor.org/rfc/rfc7231#section-7.1.4
In short - caches have to take headers into consideration when server indicates so with "Vary" header. Alternative way for server is to use Cache-control header directives.
Seems that in your case server is returning incorrect responses. If you can, fix that, if not you can not use http cache.