泽西岛:默认缓存控制为无缓存

2022-09-02 00:42:31

在编写 RESTful Web 服务时,如果我在客户端(当前是 .NET 厚客户端)上启用任何类型的缓存,则会遇到问题。默认情况下,Jersey 不发送任何类型的缓存控件标头,因此客户端会自动缓存大多数页面(这似乎是有效的行为)。

我希望让泽西岛默认发送“无缓存”的缓存控件,然后特别响应覆盖缓存控件。

有没有办法用泽西岛做到这一点?

我发现RESTeasy能够使用@NoCache注释来指定整个类的设置,但我没有找到与泽西岛类似的东西。


答案 1

使用 ResourceFilterFactory,使用 Jersey 很容易做到这一点 - 您可以创建附加到方法的任何自定义注释来设置缓存控制设置。当应用程序初始化时,资源过滤器会为每个发现的资源方法调用 - 在 ResourceFilterFactory 中,您可以检查该方法是否具有@CacheControlHeader注释(或您想要调用它的任何内容) - 如果没有,只需返回响应过滤器,将“no-cache”指令添加到响应中,否则它应该使用注释中的设置。下面是如何做到这一点的示例:

public class CacheFilterFactory implements ResourceFilterFactory {
    private static final List<ResourceFilter> NO_CACHE_FILTER = Collections.<ResourceFilter>singletonList(new CacheResponseFilter("no-cache"));

    @Override
    public List<ResourceFilter> create(AbstractMethod am) {
        CacheControlHeader cch = am.getAnnotation(CacheControlHeader.class);
        if (cch == null) {
            return NO_CACHE_FILTER;
        } else {
            return Collections.<ResourceFilter>singletonList(new CacheResponseFilter(cch.value()));
        }
    }

    private static class CacheResponseFilter implements ResourceFilter, ContainerResponseFilter {
        private final String headerValue;

        CacheResponseFilter(String headerValue) {
            this.headerValue = headerValue;
        }

        @Override
        public ContainerRequestFilter getRequestFilter() {
            return null;
        }

        @Override
        public ContainerResponseFilter getResponseFilter() {
            return this;
        }

        @Override
        public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
            // attache Cache Control header to each response based on the annotation value
            response.getHttpHeaders().putSingle(HttpHeaders.CACHE_CONTROL, headerValue);
            return response;
        }
    }
}

注释可以如下所示:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheControlHeader {
    String value();
}

通过在 Web.xml 中 Jersey servlet 的定义中添加以下 init 参数,可以在您的应用程序中注册 ResourceFilterFactory:

<init-param>
    <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
    <param-value>package.name.CacheFilterFactory</param-value>
</init-param>

答案 2

基于@martin-matula的解决方案,我创建了两个缓存注释。一个用于根本不缓存,一个用于特定缓存。采用两个参数,因此您不必自己计算秒数:@NoCache@CacheMaxAgeCacheMaxAge

@GET
@CacheMaxAge(time = 10, unit = TimeUnit.MINUTES)
@Path("/awesome")
public String returnSomethingAwesome() {
    ...
}

ResourceFilter 现在具有此创建方法,默认情况下不会干扰(因此其他缓存机制继续工作):

@Override
public List<ResourceFilter> create(AbstractMethod am) {
    if (am.isAnnotationPresent(CacheMaxAge.class)) {
        CacheMaxAge maxAge = am.getAnnotation(CacheMaxAge.class);
        return newCacheFilter("max-age: " + maxAge.unit().toSeconds(maxAge.time()));
    } else if (am.isAnnotationPresent(NoCache.class)) {
        return newCacheFilter("no-cache");
    } else {
        return Collections.emptyList();
    }
}

private List<ResourceFilter> newCacheFilter(String content) {
    return Collections
            .<ResourceFilter> singletonList(new CacheResponseFilter(content));
}

您可以在我的博客文章中看到完整的解决方案。

感谢您的解决方案马丁!