Spring MVC REST中的ETag处理

时间:2015-10-21 15:43:42

标签: java rest spring-mvc jax-rs http-etag

我正在考虑从使用JAX RS的Apache CXF RS切换到Spring MVC REST,并看到Spring MVC REST当前处理ETag的方式存在一些问题。也许我不理解正确,或者有更好的方法来实现JAX RS目前的工作?

使用Apache CXF RS,在REST服务中评估上次修改时间戳和ETag的条件(条件评估实际上非常复杂,请参阅RFC 2616第14.24和14.26节,所以我很高兴这对我来说已经完成) 。代码看起来像这样:

@GET
@Path("...")
@Produces(MediaType.APPLICATION_JSON)
public Response findBy...(..., @Context Request request) {
       ... result = ...fetch-result-or-parts-of-it...;
       final EntityTag eTag = new EntityTag(computeETagValue(result), true);
       ResponseBuilder builder = request.evaluatePreconditions(lastModified, eTag);
       if (builder == null) {
              // a new response is required, because the ETag or time stamp do not match
              // ...potentially fetch full result object now, then:
              builder = Response.ok(result);
       } else {
              // a new response is not needed, send "not modified" status without a body
       }
       final CacheControl cacheControl = new CacheControl();
       cacheControl.setPrivate(true); // store in private browser cache of user only
       cacheControl.setMaxAge(15); // may stay unchecked in private browser cache for 15s, afterwards revalidation is required
       cacheControl.setNoTransform(true); // proxies must not transform the response
       return builder
              .cacheControl(cacheControl)
              .lastModified(lastModified)
              .tag(eTag)
              .build();
}

我对Spring MVC REST的同样尝试看起来像这样:

@RequestMapping(value="...", produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<...> findByGpnPrefixCacheable(...) {
       ... result = ...fetch-result...;
//     ... result = ...fetch-result-or-parts-of-it...; - can't fetch parts, must obtain full result, see below
       final String eTag = "W/\""+computeETagValue(result)+"\""; // need to manually construct, as opposed to convenient JAX RS above
       return ResponseEntity
              .ok() // always say 'ok' (200)?
              .cacheControl(
                     CacheControl
                           .cachePrivate()
                           .maxAge(15, TimeUnit.SECONDS)
                           .noTransform()
                     )
              .eTag(eTag)
              .body(result); // ETag comparison takes place outside controller(?), but data for a full response has already been built - that is wasteful!
}

即使不需要,我也必须预先为完整响应构建所有数据。这使得在Spring MVC REST中使用的ETag远远不如它有价值。根据我的理解,弱ETag的想法是,建立其价值并进行比较可能“便宜”。在快乐的情况下,这可以防止服务器上的负载。在这种情况下,资源被修改了,当然必须建立完整的响应。

在我看来,按照设计,Spring MVC REST目前需要构建完整的响应数据,无论是否最终需要响应主体。

总而言之,Spring MVC REST有两个问题:

  1. 是否等同于evaluatePreconditions()?
  2. 有没有更好的构建ETag字符串的方法?
  3. 感谢您的想法!

1 个答案:

答案 0 :(得分:9)

  1. ,有一种等效方法。
  2. 你可以像这样使用它:

    @RequestMapping
    public ResponseEntity<...> findByGpnPrefixCacheable(WebRequest request) {
    
        // 1. application-specific calculations with full/partial data
        long lastModified = ...; 
        String etag = ...;
    
        if (request.checkNotModified(lastModified, etag)) {
            // 2. shortcut exit - no further processing necessary
            //  it will also convert the response to an 304 Not Modified
            //  with an empty body
            return null;
        }
    
        // 3. or otherwise further request processing, actually preparing content
        return ...;
    }
    

    请注意,checkNotModified方法有不同版本,包括lastModified,ETag或两者。

    您可以在此处找到文档:Support for ETag and Last-Modified response headers

    1. ,但您必须先更改一些内容。
    2. 您可以更改计算ETag的方式,这样您就不需要获取完整的结果。

      例如,如果此fetch-result是数据库查询,则可以向实体添加版本字段并使用@Version对其进行注释,以便每次修改时都会增加,然后使用该值对于ETag。

      这取得了什么成果?因为您可以将提取配置为延迟,所以您不需要检索实体的所有字段,也可以避免使用哈希来构建ETag。只需将该版本用作ETag字符串。

      以下是关于Using @Version in Spring Data project的问题。