Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to selectively disable cache for spring boot (manifest.appcache)

From this question it shows that spring security manages cache for spring boot. From the spring boot documentation it shows how to set cache for resources using:

spring.resources.cache-period= # cache timeouts in headers sent to browser

The cache-period is great for all the predefined static locations for spring boot (i.e. /css**, /js/**, /images/**) but I'm also generating a manifest.appcache for offline downloading of my static assets and due to all the above spring security/boot sends back cache headers with the manifest.appcache

"method": "GET",
"path": "/manifest.appcache",
"response": {
    "X-Application-Context": "application:local,flyway,oracle,kerberos:8080",
    "Expires": "Tue, 06 Oct 2015 16:59:39 GMT",
    "Cache-Control": "max-age=31556926, must-revalidate",
    "status": "304"
}

I'd like to know how to add an exclusion for manifest.appcache. IE and Chrome seem to 'do the right thing' with appcache regardless of my headers, but FF seems to be a little more peculiar in noting when the appcache has changed and I'm thinking my cache headers are screwing it up.

EDIT: I should add from the source for WebMvcAutoConfiguration it shows how the cache is setup for the resources, I'm just unsure how to selectively disable for my 1 case w/o potentially disrupting the rest of what spring boot sets up in this file.

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
            return;
        }

        Integer cachePeriod = this.resourceProperties.getCachePeriod();
        if (!registry.hasMappingForPattern("/webjars/**")) {
            registry.addResourceHandler("/webjars/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/")
                    .setCachePeriod(cachePeriod);
        }
        if (!registry.hasMappingForPattern("/**")) {
            registry.addResourceHandler("/**")
                    .addResourceLocations(RESOURCE_LOCATIONS)
                    .setCachePeriod(cachePeriod);
        }
    }
like image 479
JimB Avatar asked Oct 06 '14 12:10

JimB


3 Answers

Based on this answer detailing IE needing "max-age=1, must-revalidate", and through testing on all browsers, setting a properties value of

spring.resources.cache-period=1

will allow the proper http headers to be written which allow the appcache manifest to be handled properly. it's not the solution I hoped for (being able to have a cache-period of 0 with proper headers is what i wanted) but it does make the browser perform properly and utilize the appcache manifest properly.

Again to summarize the solution context - this is for my app that downloads all my assets offline (js/css/html) and serves from appcache.

like image 170
JimB Avatar answered Jan 01 '23 13:01

JimB


Thanks for the Q and the A!

We had similar problems: All browsers (Chrome, Safari, IE) but one (FF) didn't cache the manifest files themselves but reloaded it after a change.

However, setting the cache period setting in Spring Boot didn't solve the problem completely. Additionally I didn't want to set cache-control parameters for all files served by the Spring Boot application, but only to disable caching for the manifest.

My solution consists of of two parts:

  1. Provide a "rev" comment in the Manifest file. Though not covered by the W3C spec some browsers seem to want a comment like this to detect changes in the manifest or the referenced (cached) files:

    # rev 7
    

    We now change the "rev" parameter in the cache manifest file by a Maven generated unique build number: https://github.com/dukecon/dukecon_html5/commit/b60298f0b856a7e54c97620f278982142e3e1f45).

  2. Disable caching by providing a Filter which adds a header "Cache-Control: no-cache" to exactly the cache.manifest file pattern: https://github.com/dukecon/dukecon_server/commit/dc02f26996cb172df804da007546f439df75126d
like image 28
Gerd Aschemann Avatar answered Jan 01 '23 14:01

Gerd Aschemann


In current version (Feb 2016) there is no need to do something in code to change default behavior. Just do some configuration in your application.properties:

# Enable HTML5 application cache manifest rewriting.
spring.resources.chain.html-application-cache=true

# Enable the Spring Resource Handling chain. Disabled by default unless at least one strategy has been enabled.
spring.resources.chain.enabled=true
# Enable the content Version Strategy.
spring.resources.chain.strategy.content.enabled=true 
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.content.paths=/** 

# Locations of static resources.
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/

That's all. Now Spring will check if your static files was changed and can send smarter responses (If-Modiffied-Since and others) and rewrite your appcache also.

Also, if there are reasons to not use content-based version for some resources - you can use alternate FixedVersion strategy and set version explicitly in your config:

#Enable the fixed Version Strategy.
spring.resources.chain.strategy.fixed.enabled=false 
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.fixed.paths= 
# Version string to use for the Version Strategy.
spring.resources.chain.strategy.fixed.version= 

P.S. Don't forget about Spring Security: it rewrite cache headers and disable caching.

See more in docs

like image 20
Alexandr Latushkin Avatar answered Jan 01 '23 15:01

Alexandr Latushkin