简体   繁体   English

如何使用CXF,JAX-RS和HTTP缓存

[英]How to use CXF, JAX-RS and HTTP Caching

The CXF documentation mentions caching as Advanced HTTP : CXF文档提到缓存为高级HTTP

CXF JAXRS provides support for a number of advanced HTTP features by handling If-Match, If-Modified-Since and ETags headers. CXF JAXRS通过处理If-Match,If-Modified-Since和ETags标头提供对许多高级HTTP功能的支持。 JAXRS Request context object can be used to check the preconditions. JAXRS请求上下文对象可用于检查前提条件。 Vary, CacheControl, Cookies and Set-Cookies are also supported. 还支持Vary,CacheControl,Cookies和Set-Cookies。

I'm really interested in using (or at least exploring) these features. 我真的很想使用(或至少探索)这些功能。 However, while "provides support" sounds really interesting, it isn't particularly helpful in implementing such features. 然而,虽然“提供支持”的声音非常有趣,但它在实现这些功能方面并不是特别有用。 Any help or pointers on how to use If-Modified-Since, CacheControl or ETags? 有关如何使用If-Modified-Since,CacheControl或ETags的任何帮助或指示?

Actually, the answer isn't specific to CXF - it's pure JAX-RS: 实际上,答案并非特定于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();
}

With the forthcoming JAX-RS 2.0 it will be possible to apply Cache-Control declaratively, as explained in http://jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0/ 使用即将推出的JAX-RS 2.0,可以声明性地应用Cache-Control,如http://jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0/中所述。

You can already test this at least with Jersey. 你已经可以测试至少使用泽西岛了。 Not sure about CXF and RESTEasy though. 虽然不确定CXF和RESTEasy。

CXF didn't implements dynamic filtering as explained here : http://www.jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0 CXF没有实现动态过滤,如下所述: http//www.jalg.net/2012/09/declarative-cache-control-with-jax-rs-2-0

And if you use to return directly your own objects and not CXF Response, it's hard to add a cache control header. 如果您使用直接返回自己的对象而不是CXF Response,则很难添加缓存控制头。

I find an elegant way by using a custom annotation and creating a CXF Interceptor that read this annotation and add the header. 我通过使用自定义注释并创建一个读取此注释并添加标题的CXF拦截器来找到一种优雅的方法。

So first, create a CacheControl annotation 首先,创建一个CacheControl注释

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

Then, add this annotation to your CXF operation method (interface or implementation it works on both if you use an interface) 然后,将此批注添加到您的CXF操作方法(如果您使用接口,它可以在两者上工作的接口或实现)

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

Then create a CacheControl interceptor that will handle the annotation and add the header to your response. 然后创建一个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()));
    }
}

Finally configure CXF to use your interceptor, you can find all the needed information here : http://cxf.apache.org/docs/interceptors.html 最后配置CXF使用你的拦截器,你可以在这里找到所有需要的信息: http//cxf.apache.org/docs/interceptors.html

Hope it will help. 希望它会有所帮助。

Loïc 卢瓦克

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM