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
WebMvcConfigurer
to work properly though so that certain resources are not cached. I created a class namedStaticAssetsResolverConfig
(in a package under/src/main/groovy
) that implementsWebMvcConfigurer
and has theorg.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 toregistry.addResourceHandler()
:'/static/ui5/com/**/*.xml', '/static/ui5/com/**/*.js'
Any tips on how to get this working would be greatly appreciated!