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:
- First saw this issue - https://github.com/grails/grails-core/issues/10410#issuecomment-611572172
- Deploying SPA combined with Grails Guide - https://guides.grails.org/grails-vue-combined/guide/index.html
Top comments (1)
Thank you! My Grails 4.0.1 application is now having my static assets cached by the browser.
I'm having trouble getting the
WebMvcConfigurerto work properly though so that certain resources are not cached. I created a class namedStaticAssetsResolverConfig(in a package under/src/main/groovy) that implementsWebMvcConfigurerand has theorg.springframework.stereotype.Componentannotation.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
StaticAssetsResolverConfigis 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=100response header when requested. I'm using the following path pattern parameters in my call toregistry.addResourceHandler():'/static/ui5/com/**/*.xml', '/static/ui5/com/**/*.js'Any tips on how to get this working would be greatly appreciated!