webentwicklung-frage-antwort-db.com.de

Jersey: Standard-Cache-Kontrolle auf No-Cache

Beim Schreiben eines RESTful-Webdiensts stoßen Sie auf Probleme, wenn ich auf meinem Client (derzeit ein .NET Thick Client) eine Art Zwischenspeicherung aktiviere. Standardmäßig sendet Jersey keine Art von Header für die Cache-Steuerung, sodass der Client die meisten Seiten automatisch zwischenspeichert (was ein gültiges Verhalten zu sein scheint).

Ich möchte, dass Jersey standardmäßig eine Cache-Kontrolle von "no-cache" sendet, und dann insbesondere die Antworten die Cache-Kontrolle überschreiben.

Gibt es eine Möglichkeit, dies mit Jersey zu tun?

Ich habe herausgefunden, dass RESTeasy die Möglichkeit hat, die @NoCache-Annotation zu verwenden, um die Einstellung für die gesamte Klasse festzulegen, aber ich habe mit Jersey nichts Ähnliches gefunden.

20
Pete

Mit Jersey ist dies mit Hilfe einer ResourceFilterFactory ganz einfach. Sie können beliebige benutzerdefinierte Anmerkungen erstellen, die Sie mit Ihren Methoden verknüpfen, um Einstellungen für die Cache-Steuerung festzulegen. ResourceFilterFactories wird für jede erkannte Ressourcenmethode aufgerufen, wenn die Anwendung initialisiert wird. In Ihrer ResourceFilterFactory können Sie überprüfen, ob die @CacheControlHeader-Annotation der Methode vorhanden ist (oder wie auch immer Sie sie aufrufen möchten) "Direktive zur Antwort, sonst sollte es die Einstellungen aus der Annotation verwenden. Hier ist ein Beispiel, wie das geht:

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

Die Anmerkung kann so aussehen:

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

Die ResourceFilterFactory kann in Ihrer Anwendung registriert werden, indem Sie der Definition des Jersey-Servlets in web.xml den folgenden init-Parameter hinzufügen:

<init-param>
    <param-name>com.Sun.jersey.spi.container.ResourceFilters</param-name>
    <param-value>package.name.CacheFilterFactory</param-value>
</init-param>
23
Martin Matula

Basierend auf der Lösung von @ martin-matula habe ich zwei Cache-Anmerkungen erstellt. Ein @NoCache für überhaupt kein Caching und ein @CacheMaxAge für spezifisches Caching. Die Variable CacheMaxAge benötigt zwei Argumente, damit Sie die Sekunden nicht selbst berechnen müssen:

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

Der ResourceFilter verfügt jetzt über diese Create-Methode, die standardmäßig nicht interferiert (andere Caching-Mechanismen funktionieren also weiterhin):

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

Sie können die vollständige Lösung in meinem Blogpost sehen. 

Danke für die Lösung Martin!

14
Alex

Die Lösung von @ martin-matula funktioniert nicht mit JAX-RS 2.0/Jersey 2.x, da ResourceFilterFactory und ResourceFilter entfernt wurden. Die Lösung kann wie folgt an JAX-RS 2.0 angepasst werden.

Anmerkung:

@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 muss bei Jersey registriert sein. Ich mache das über Dropwizard - mithilfe von environment.jersey (). Register () -, aber ich verstehe, dass dies auf Standalone-Systemen der Fall ist, indem Sie Jersey Ihre Klassen nach @Provider-Anmerkungen scannen lassen, indem Sie Folgendes in Ihrer web.xml definieren :

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

In diesem Beitrag finden Sie weitere Informationen zum Registrieren von Komponenten.

7
JanneK

Ich denke, du kannst das benutzen

isNoCache(true)

das wird das Cachen im Browser beenden.

Sehen: 

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

Hoffe das hilft.

2
Stephen

Ich habe eine Anmerkung gefunden, die das Zwischenspeichern deaktivieren kann. Sie können die folgenden Anmerkungen für Ihre API verwenden:

@CacheControl(noCache = true)

Ref: Jersey-Anmerkung zur Cache-Steuerung

0
Sahil Bhavsar