【问题标题】:How to use CXF, JAX-RS and HTTP Caching如何使用 CXF、JAX-RS 和 HTTP 缓存
【发布时间】:2011-01-06 07:46:44
【问题描述】:

CXF 文档提到缓存为 Advanced HTTP

CXF JAXRS 通过处理 If-Match、If-Modified-Since 和 ETags 标头提供对许多高级 HTTP 功能的支持。 JAXRS 请求上下文对象可用于检查先决条件。还支持 Vary、CacheControl、Cookies 和 Set-Cookies。

我对使用(或至少探索)这些功能非常感兴趣。然而,虽然“提供支持”听起来很有趣,但它对于实现这些功能并不是特别有帮助。有关如何使用 If-Modified-Since、CacheControl 或 ETags 的任何帮助或指示?

【问题讨论】:

    标签: java rest cxf jax-rs http-caching


    【解决方案1】:

    实际上,答案并不特定于 CXF - 它是纯粹的 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();
    }
    

    【讨论】:

    • 很好的答案。我唯一的评论是您生成的 EntityTag 可能不需要此人的 UUID。唯一重要的是同一资源的修订版之间的 eTag 更改。假设 ID 是不可变的,UUID 与资源的路径是冗余的(尽管您的 Impl 将该参数称为“名称”,所以它可能不是不可变的。此外,您应该确保该值是特定于表示的。例如,如果资源的两种表示形式因媒体类型而异,请使用媒体类型值和版本标识符来制作特定于表示形式的 ETag 值。
    • 我从不使用 Response 对象——让 CXF 处理那部分。没有它你会怎么做?
    • @oligofren 我以前从未使用过它们,但这是我找到的唯一解决方案。
    • 好的。我对此提出了一个新问题。也许其他人知道:) stackoverflow.com/questions/11317543/…
    【解决方案2】:

    在即将推出的 JAX-RS 2.0 中,可以以声明方式应用 Cache-Control,如 http://jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0/ 中所述

    您至少可以使用 Jersey 进行测试。不过不确定 CXF 和 RESTEasy。

    【讨论】:

    • 这更像是声明式地应用过滤器,可以做缓存之类的事情,但无论如何都是一个很大的改进。感谢您让我(我们)知道。
    【解决方案3】:

    CXF 没有按照此处的说明实现动态过滤:http://www.jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0

    如果你使用直接返回你自己的对象而不是CXF响应,那么很难添加缓存控制头。

    我找到了一种优雅的方法,即使用自定义注释并创建一个读取此注释并添加标题的 CXF 拦截器。

    所以首先,创建一个 CacheControl 注解

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

    然后,将此注释添加到您的 CXF 操作方法(如果您使用接口,则接口或实现都适用)

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

    然后创建一个 CacheControl 拦截器来处理注释并将标头添加到您的响应中。

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

    最后配置 CXF 使用你的拦截器,你可以在这里找到所有需要的信息:http://cxf.apache.org/docs/interceptors.html

    希望它会有所帮助。

    洛伊克

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-09
      • 1970-01-01
      • 2013-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多