1

I have an Api endpoint with Spring 5 as @RestController and I want to specify the Cache-Control for all endpoints at once (via a Config or an Annotation,...).

Currently I do this on every single method. I also had to use ResponseEntity to set the Cache-Control which bloats the code even more.

@RestController
@RequestMapping("/api")
public class MyApi {

    @PostMapping("/search")
    public ResponseEntity<SearchResultDto> search(SearchDto searchDto) {
        // ...
        return ResponseEntity.ok()
                .cacheControl(CacheControl.maxAge(20, TimeUnit.SECONDS))
                .body(searchResultDto);
    }

    @GetMapping("/get")
    public ResponseEntity<EntityDto> get(Long id) {
        // ...
        return ResponseEntity.ok()
                .cacheControl(CacheControl.maxAge(20, TimeUnit.SECONDS))
                .body(entityDto);
    }

    // ... more methods

}
  • I tried Aspects, but it is not possible to alter the http headers after they are created.
@Aspect
@Component
public class CacheAspect {

    @Pointcut("within(io.company.MyApi)")
    public void apiMethods() {
    }

    @Around("apiMethods()")
    public Object addCacheControlOnResponseEntity(ProceedingJoinPoint pjp) throws Throwable {
        Object retval = pjp.proceed();

        if (retval instanceof ResponseEntity) {
            ((ResponseEntity) retval).getHeaders().setCacheControl(CacheControl.maxAge(15, TimeUnit.SECONDS));
            // exception is thrown, because map can not be modified anymore
        }

        return retval;
    }
}
  • I tried interceptors (HandlerInterceptor), but here it is the same problem, that the http headers can not be modified afterwards.
@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new HandlerInterceptor() {
            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                response.setHeader(HttpHeaders.CACHE_CONTROL, CacheControl.maxAge(2, TimeUnit.SECONDS).getHeaderValue());
                // header does not get modified
            }

            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
                response.setHeader(HttpHeaders.CACHE_CONTROL, CacheControl.maxAge(2, TimeUnit.SECONDS).getHeaderValue());
                // header does not get modified
            }
        });
    }
}
  • I tried to add a resource handler (ResourceHandler) (for usually static content) and set the caching strategy for /api/**, but it was not applied.
@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/api/**")
                .setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS));
        // didn't had any effect
    }
}

... I am out of ideas

What can I do to write less duplicate/boilerplate code?

mangei
  • 747
  • 7
  • 16
  • Using Interceptor looks like a good solution. Maybe you could try changing the response in the preHandle, but I am not sure it would suit your need. Else, you should wrap the response as suggested in this post: https://stackoverflow.com/questions/30702970/spring-modifying-headers-for-every-request-after-processing-in-posthandle – Nicolas Barlogis May 02 '18 at 08:48

0 Answers0