loading...

Caching SPA static assets in Grails

erichelgeson profile image Eric Helgeson ・1 min read

If you deploy static assets embedded in your Grails 3 or 4 apps you might notice by default they are not cached - meaning every time a user loads your site - they re-download each asset.

Grails does have a configuration to cache static assets:

application.yml

grails:
    resources:
        cachePeriod: 3600 

The problem with this is the entry point index.html is cached as well meaning users won't see updates until after the cache period has elapsed.

The solution is to add a ResourceHandler that matches index.html and sets the cachePeriod to 0

@Component // or add to resources.groovy
class SpaResolverConfig implements WebMvcConfigurer { // WebMvcConfigurerAdapter for Grails 3.x

    // Copied from the GrailsWebMvcConfigurer 
    //  - https://github.com/grails/grails-core/blob/28556c0a1a01d2958d6d3aed251dcacc2a6e38da/grails-plugin-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersGrailsPlugin.groovy#L179-L193
    private static final String[] RESOURCE_LOCATIONS = [ "/", "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" ]

    @Override
    void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler('/static/**/index.html') // ant matcher pattern
                .addResourceLocations(RESOURCE_LOCATIONS)
                .setCachePeriod(0) // <-- no-cache
    }
}

Now css/javascript/images from the SPA are cached for 3600 seconds while the index.html file is never cached.

Note: This has no affect on assets using the asset-pipeline plugin - those are cached correctly.

Links:

Discussion

pic
Editor guide
 

Thank you! My Grails 4.0.1 application is now having my static assets cached by the browser.

I'm having trouble getting the WebMvcConfigurer to work properly though so that certain resources are not cached. I created a class named StaticAssetsResolverConfig (in a package under /src/main/groovy) that implements WebMvcConfigurer and has the org.springframework.stereotype.Component annotation.

gist.github.com/tylervz/bdb2c07a34...

First, is there a different place I need to put my class in order for it to get recognized by Grails and get executed? I needed to register it as a bean in resources.groovy (as you noted was an option) for it run. The good news is that I know StaticAssetsResolverConfig is running, so it's not too big of a deal. In case anyone else is wondering, this is how I defined the bean in resources.groovy:

staticAssetsResolverConfig(StaticAssetsResolverConfig)

Second, the resources I want to not have cached still have a Cache-Control: max-age=100 response header when requested. I'm using the following path pattern parameters in my call to registry.addResourceHandler():

'/static/ui5/com/**/*.xml', '/static/ui5/com/**/*.js'

Any tips on how to get this working would be greatly appreciated!