2010-01-18 12 views
17

La documentación CXF menciona el almacenamiento en caché como Advanced HTTP:Cómo utilizar CXF, JAXRS y almacenamiento en caché HTTP

CXF JAXRS proporciona soporte para una serie de características avanzadas por el manejo de HTTP If-Match, If-Modified-Since y encabezados ETags. El objeto de contexto de solicitud JAXRS se puede usar para verificar las condiciones previas. Vary, CacheControl, Cookies y Set-Cookies también son compatibles.

Estoy realmente interesado en usar (o al menos explorar) estas funciones. Sin embargo, aunque "proporciona soporte" suena realmente interesante, no es particularmente útil para implementar tales características. ¿Alguna ayuda o sugerencias sobre cómo usar If-Modified-Since, CacheControl o ETags?

Respuesta

27

En realidad, la respuesta no es específico de CXF - es pura JAX-RS:

// IPersonService.java 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.PathParam; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.Request; 
import javax.ws.rs.core.Response; 

@GET 
@Path("/person/{id}") 
Response getPerson(@PathParam("id") String id, @Context Request request); 


// PersonServiceImpl.java 
import javax.ws.rs.core.CacheControl; 
import javax.ws.rs.core.EntityTag; 
import javax.ws.rs.core.Request; 
import javax.ws.rs.core.Response; 
import javax.ws.rs.core.Response.ResponseBuilder; 

public Response getPerson(String name, Request request) { 
    Person person = _dao.getPerson(name); 

    if (person == null) { 
    return Response.noContent().build(); 
    } 

    EntityTag eTag = new EntityTag(person.getUUID() + "-" + person.getVersion()); 

    CacheControl cc = new CacheControl(); 
    cc.setMaxAge(600); 

    ResponseBuilder builder = request.evaluatePreconditions(person.getUpdated(), eTag); 

    if (builder == null) { 
    builder = Response.ok(person); 
    } 

    return builder.cacheControl(cc).lastModified(person.getUpdated()).build(); 
} 
+4

Great answer. Mi único comentario es que la EntityTag que generas probablemente no necesita el UUID de la persona. Solo es importante que un eTag cambie entre las revisiones del mismo recurso. Suponiendo que la ID es inmutable, UUID es redundante con la ruta al recurso (aunque su Impl llama a ese parámetro "nombre", por lo que tal vez no sea inmutable. Además, debe asegurarse de que este valor sea específico de la representación. dos representaciones de un recurso varían según el tipo de medio, utilice el valor de tipo de medio junto con el identificador de versión para crear un valor ETag específico de representación. – benvolioT

+0

Nunca utilizo un objeto Response; simplemente deje que CXF maneje esa parte. ¿Cómo lo haría? sin él? – oligofren

+0

@oligofren Nunca los había usado antes, pero esa fue la única solución que encontré. – sfussenegger

5

Con las próximas JAX-RS 2.0 será posible aplicar Cache-Control declarativa, como se explica en http://jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0/

Ya puede probar esto al menos con Jersey. Sin embargo, no estoy seguro acerca de CXF y RESTEasy.

+0

Se parece más a declarativamente aplicar filtros que pueden hacer cosas tales como el almacenamiento en caché, pero de todos modos una gran mejora. Gracias por dejarnos (nosotros) saber. – sfussenegger

0

CXF no implementa el filtrado dinámico como se explica aquí: http://www.jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0

Y si se utiliza para volver directamente a sus propios objetos y no CXF respuesta, es difícil añadir una cabecera de control de caché.

Encuentro una manera elegante mediante el uso de una anotación personalizada y la creación de un Interceptor CXF que lee esta anotación y agrega el encabezado.

Así que en primer lugar, crear una anotación CacheControl

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface CacheControl { 
    String value() default "no-cache"; 
} 

A continuación, añadir esta anotación a su método de operación CXF (interfaz o aplicación que funciona tanto si se utiliza una interfaz)

@CacheControl("max-age=600") 
public Person getPerson(String name) { 
    return personService.getPerson(name); 
} 

Entonces crea un interceptor CacheControl que manejará la anotación y agregará el encabezado a tu respuesta.

public class CacheInterceptor extends AbstractOutDatabindingInterceptor{ 
    public CacheInterceptor() { 
     super(Phase.MARSHAL); 
    } 

    @Override 
    public void handleMessage(Message outMessage) throws Fault { 
     //search for a CacheControl annotation on the operation 
     OperationResourceInfo resourceInfo = outMessage.getExchange().get(OperationResourceInfo.class); 
     CacheControl cacheControl = null; 
     for (Annotation annot : resourceInfo.getOutAnnotations()) { 
      if(annot instanceof CacheControl) { 
       cacheControl = (CacheControl) annot; 
       break; 
      } 
     } 

     //fast path for no cache control 
     if(cacheControl == null) { 
      return; 
     } 

     //search for existing headers or create new ones 
     Map<String, List<String>> headers = (Map<String, List<String>>) outMessage.get(Message.PROTOCOL_HEADERS); 
     if (headers == null) { 
      headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 
      outMessage.put(Message.PROTOCOL_HEADERS, headers); 
     } 

     //add Cache-Control header 
     headers.put("Cache-Control", Collections.singletonList(cacheControl.value())); 
    } 
} 

Finalmente configurar CXF para utilizar su interceptor, se puede encontrar toda la información necesaria aquí: http://cxf.apache.org/docs/interceptors.html

esperan que ayude.

Loïc

Cuestiones relacionadas