Jersey:默认缓存控制为无缓存缓存、Jersey

2023-09-06 11:33:25 作者:相顾不相识

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

While writing a RESTful web service, I am encountering issues if I enable any sort of caching on my client (currently a .NET thick client). By default Jersey is not sending any sort of cache control header, so the client is caching most pages automatically (which seems to be valid behaviour).

我希望 Jersey 默认发送无缓存"的缓存控制,然后特别是响应会覆盖缓存控制.

I would like to have Jersey by default send a cache control of "no-cache", and then in particular responses override the cache control.

有什么方法可以用泽西岛做到这一点?

Is there any way to do this with Jersey?

我发现 RESTeasy 有能力使用@NoCache 注解来指定整个类的设置,但是我没有发现任何与 Jersey 类似的东西.

I've found that RESTeasy has the ability to use the @NoCache annotation to specify the setting for the whole class, but I've not found anything similar with Jersey.

推荐答案

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

This is easy with Jersey by using a ResourceFilterFactory - you can create any custom annotation you attach to your methods to set cache control settings. ResourceFilterFactories get called for each discovered resource method when the application initializes - in your ResourceFilterFactory you can check if the method has your @CacheControlHeader annotation (or whatever you want to call it) - if not, simply return response filter that adds "no-cache" directive to the response, otherwise it should use the settings from the annotation. Here is an example of how to do that:

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 参数来注册到您的应用程序中:

The ResourceFilterFactory can be registered in your application by adding the following init param to the definition of Jersey servlet in web.xml:

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