【问题标题】:ETag handling in Spring MVC RESTSpring MVC REST 中的 ETag 处理
【发布时间】:2016-01-20 16:55:36
【问题描述】:

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

使用 Apache CXF RS,最后修改时间戳和 ETag 的条件在 REST 服务内进行评估(条件评估实际上相当复杂,请参阅 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 字符串?

感谢您的意见!

【问题讨论】:

  • 好问题。我认为如果 Spring 不提供开箱即用的功能,您可以扩展 ResponseEntity 以执行回调以构建整个主体。
  • 看起来你需要在任何一种情况下预加载result......但我明白了,在第一种情况下,你只需要一些便宜的东西(例如版本号)来计算 ETag。我想最好的答案是懒惰的健身。
  • 最终,在某些应用程序中,为了获得最佳性能,应用程序必须自行测试条件。这并不那么难。 This 是我的逻辑实现,基于对最新 HTTP 规范的讨论。
  • 可能是你在第一行加倍“切换自”

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


【解决方案1】:
  1. 是的,有一个等效的方法。

你可以这样使用它:

@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. 是的,但您必须先更改一些内容。

您可以更改计算 ETag 的方式,从而无需获取完整结果。

例如,如果这个 fetch-result 是一个数据库查询,您可以向您的实体添加一个版本字段并使用 @Version 对其进行注释,以便每次修改时递增,然后将该值用于电子标签。

这有什么作用?由于您可以将获取配置为惰性,因此您无需检索实体的所有字段,并且还避免了必须对其进行散列以构建 ETag。只需使用版本作为 ETag 字符串。

这是一个关于Using @Version in Spring Data project的问题。

【讨论】:

  • 我们能否在 REST 调用中将 WebRequest 与 @RequestBody 或 HttpEntity 结合使用?我有一个疑问,我们如何计算具有正文和标题的 REST 调用中的 Etag...
  • 我认为 Etag 仅用于 GET 调用。 WebRequest 处理所有的 GET 请求。 Etag 不适用于 POST、PUT 等...我说的对吗?
猜你喜欢
  • 1970-01-01
  • 2015-09-01
  • 2013-09-21
  • 2012-08-17
  • 1970-01-01
  • 1970-01-01
  • 2021-09-06
  • 1970-01-01
  • 2014-04-05
相关资源
最近更新 更多