在编写 RESTful Web 服务时,如果我在我的客户端(当前是一个 .NET 胖客户端)上启用任何类型的缓存,我会遇到问题。默认情况下,Jersey 不发送任何类型的缓存控制标头,因此客户端会自动缓存大多数页面(这似乎是有效的行为)。
我希望泽西岛默认发送“无缓存”的缓存控制,然后特别是响应覆盖缓存控制。
有什么办法可以用泽西岛做到这一点?
我发现 RESTeasy 能够使用 @NoCache 注释来指定整个类的设置,但我没有发现任何与 Jersey 类似的东西。
通过使用 ResourceFilterFactory,Jersey 很容易做到这一点 - 您可以创建任何附加到方法的自定义注释来设置缓存控制设置。当应用程序初始化时,每个发现的资源方法都会调用 ResourceFilterFactories - 在您的 ResourceFilterFactory 中,您可以检查该方法是否具有您的 @CacheControlHeader 注释(或任何您想要调用的注释) - 如果没有,只需返回添加“无缓存”的响应过滤器" 响应的指令,否则它应该使用注释中的设置。以下是如何执行此操作的示例:
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();
}
ResourceFilterFactory 可以通过将以下初始化参数添加到 web.xml 中 Jersey servlet 的定义中来在您的应用程序中注册:
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>package.name.CacheFilterFactory</param-value>
</init-param>
基于@martin-matula 的解决方案,我创建了两个缓存注释。一种@NoCache
用于完全不缓存,一种@CacheMaxAge
用于特定缓存。需要两个参数,CacheMaxAge
因此您不必自己计算秒数:
@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));
}
您可以在我的博文中看到完整的解决方案。
感谢马丁的解决方案!
@martin-matula 的解决方案不适用于 JAX-RS 2.0 / Jersey 2.x,因为 ResourceFilterFactory 和 ResourceFilter 已被删除。该解决方案可适用于 JAX-RS 2.0,如下所示。
注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheControlHeader {
String value();
}
动态特性:
@Provider
public class CacheFilterFactory implements DynamicFeature {
private static final CacheResponseFilter NO_CACHE_FILTER =
new CacheResponseFilter("no-cache");
@Override
public void configure(ResourceInfo resourceInfo,
FeatureContext featureContext) {
CacheControlHeader cch = resourceInfo.getResourceMethod()
.getAnnotation(CacheControlHeader.class);
if (cch == null) {
featureContext.register(NO_CACHE_FILTER);
} else {
featureContext.register(new CacheResponseFilter(cch.value()));
}
}
private static class CacheResponseFilter implements ContainerResponseFilter {
private final String headerValue;
CacheResponseFilter(String headerValue) {
this.headerValue = headerValue;
}
@Override
public void filter(ContainerRequestContext containerRequestContext,
ContainerResponseContext containerResponseContext) {
// attache Cache Control header to each response
// based on the annotation value
containerResponseContext
.getHeaders()
.putSingle(HttpHeaders.CACHE_CONTROL, headerValue);
}
}
}
CacheFilterFactory 需要在 Jersey 注册。我正在通过 Dropwizard 执行此操作 - 使用 environment.jersey().register() - 但在独立系统上,我知道这可以通过让 Jersey 扫描您的类以查找 @Provider 注释来完成,方法是在您的 web.xml 中定义以下内容:
<servlet>
<servlet-name>my.package.MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<!-- Register resources and providers under my.package. -->
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.package</param-value>
</init-param>
</servlet>
有关注册组件的更多信息,请参阅这篇文章。
我认为你可以使用
isNoCache(true)
这将停止在浏览器中缓存。
看:
http://jersey.java.net/nonav/apidocs/1.12/jersey/javax/ws/rs/core/CacheControl.html#isNoCache%28%29
希望这可以帮助。