Jersey:默认缓存控制到无缓存

时间:2012-06-07 14:55:34

标签: java rest jersey jax-rs

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

我想让Jersey默认发送一个“no-cache”的缓存控件,然后特别是响应覆盖缓存控件。

有没有办法用泽西岛做这个?

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

5 个答案:

答案 0 :(得分:23)

使用ResourceFilterFactory可以轻松使用Jersey - 您可以创建附加到方法的任何自定义注释来设置缓存控制设置。在应用程序初始化时,为每个发现的资源方法调用ResourceFilterFactories - 在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();
}

可以在应用程序中注册ResourceFilterFactory,方法是将以下init参数添加到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>

答案 1 :(得分:14)

根据@ martin-matula的解决方案,我创建了两个Cache注释。一个@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));
}

您可以看到完整的解决方案in my blogpost

感谢Martin的解决方案!

答案 2 :(得分:7)

@ 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();
}

DynamicFeature:

@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通过在web.xml中定义以下内容来扫描类的@Provider注释来完成。 :

<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>

有关注册组件的更多信息,请参阅this post

答案 3 :(得分:2)

我认为你可以使用

isNoCache(true)

将停止在浏览器中缓存。

见:

http://jersey.java.net/nonav/apidocs/1.12/jersey/javax/ws/rs/core/CacheControl.html#isNoCache%28%29

希望这有帮助。

答案 4 :(得分:0)

我找到了一个可以禁用缓存的注释。您可以对API使用以下注释:

@CacheControl(noCache = true)

价: Jersey Annotation for cache control