是否可以使用 JAX-RS 设置 ETag,而无需求助于响应对象



在我在SO上找到的关于JAX-RS和高速缓存的少数问题(带答案)之一中,生成ETag(用于高速缓存)的答案是在响应对象上设置一些值。如下:

@GET
@Path("/person/{id}")
public Response getPerson(@PathParam("id") String name, @Context 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();
}

问题是这对我们不起作用,因为我们对 SOAP 和 REST 服务使用相同的方法,通过使用 @WebMethod (SOAP)、@GET(以及我们可能需要的任何其他方法来公开服务)注释这些方法。以前的服务对我们来说是这样的(不包括标头的创建):

@WebMethod
@GET
@Path("/person/{id}")
public Person getPerson(@WebParam(name="id") @PathParam("id") String name){
  return _dao.getPerson(name);
}

有什么办法 - 通过一些额外的配置 - 设置这些标头?这是我第一次发现使用 Response 对象实际上比自动转换有一些好处......

我们正在使用 Apache CXF。

是的,如果您可以在创建响应对象后生成 E-tag,则可以使用拦截器来实现此目的。

public class MyInterceptor extends AbstractPhaseInterceptor<Message> {
    public MyInterceptor () {
        super(Phase.MARSHAL);
    }
    public final void handleMessage(Message message) {
        MultivaluedMap<String, Object> headers = (MetadataMap<String, Object>) message.get(Message.PROTOCOL_HEADERS);
        if (headers == null) {
            headers = new MetadataMap<String, Object>();
        }             
        //generate E-tag here
        String etag = getEtag();
        // 
        String cc = 600;
        headers.add("E-Tag", etag);
        headers.add("Cache-Control", cc);
        message.put(Message.PROTOCOL_HEADERS, headers);
    }
}

如果这种方式不可行,我会使用您发布的原始解决方案,只需将您的 Person 实体添加到构建器中:

Person p = _dao.getPerson(name);
return builder.entity(p).cacheControl(cc).lastModified(person.getUpdated()).build();

或者它可以像发回"错误"代码一样简单......取决于你想做什么。

@Path("/{id}")
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public ProductSearchResultBean getProductById(@PathParam("id") Integer productId, @QueryParam("expand") List<String> expand, @Context HttpServletRequest request, @Context HttpServletResponse response) throws IOException {
    ProductSearchResultBean productDetail = loadProductDetail(productId, expand);
    EntityTag etag = new EntityTag(((Integer)(productDetail.toString().hashCode())).toString());
    String otherEtag = request.getHeader("ETag");
    if(etag.getValue().equals(otherEtag)){
        response.sendError(304, "not Modified");
    }
    response.addHeader("ETag", etag.getValue());
    return productDetail;
}

无论如何,我就是这样解决的。祝你好运!(改用Spring MVC。有一个开箱即用的过滤器可以为您做所有事情......甚至制作了一个好的ETag:))

您可以考虑为此使用响应过滤器。我开发了一个气味库,完全符合您的需求:https://github.com/tobilarscheid/jaxrs-etag-filter

最新更新