首页 > 解决方案 > 根据规范,在以毫秒为单位的日期上,evaluatePreconditions 的正确行为是什么?

问题描述

在将我的 JAX-RS 应用程序从 Jersey 迁移到 Quarkus/Resteasy 时,我遇到了使用evaluatePreconditions(Date lastModified). 实际上,在我的用例中,最后修改的日期包含毫秒,不幸的是标题的日期格式If-Modified-Since并且Last-Modified不支持毫秒,正如我们在RFC 2616中看到的那样。

Jersey 从提供的日期开始修剪毫秒(如我们在此处看到的),而在 Resteasy 中,日期没有被修改,因此它实际上比较了If-Modified-Since具有不同精度(分别为秒与毫秒)的日期(来自标题的日期和提供的日期),这以不匹配而告终,因此是 HTTP 状态代码200

说明问题的代码:

@Path("/evaluatePreconditions")
public class EvaluatePreconditionsResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response findData(@Context Request request) {
        final Data data = retrieveData();
        final Date lastModified = Timestamp.valueOf(data.getLastModified());
        final Response.ResponseBuilder responseBuilder = 
            request.evaluatePreconditions(lastModified);
        if (responseBuilder == null) {
            // Last modified date didn't match, send new content
            return Response.ok(data.toString())
                .lastModified(lastModified)
                .build();
        }
        // Sending 304 not modified
        return responseBuilder.build();
    }

    private Data retrieveData() {
        // Let's assume that we call a service here that provides this value
        // The date time is expressed in GMT+2, please adjust it according 
        // to your timezone
        return new Data(
            LocalDateTime.of(2020, 10, 2, 10, 23, 16, 1_000_000), 
            "This is my content"
        );
    }

    public static class Data {
        private final LocalDateTime lastModified;
        private final String content;

        public Data(LocalDateTime lastModified, String content) {
            this.lastModified = lastModified;
            this.content = content;
        }

        public LocalDateTime getLastModified() {
            return lastModified;
        }

        @Override
        public String toString() {
            return content;
        }
    }
}

与 Jersey 对应的结果:

curl -H "If-Modified-Since: Fri, 02 Oct 2020 08:23:16 GMT" \
     -I localhost:8080/evaluatePreconditions
HTTP/1.1 304 Not Modified
...

Quarkus/Resteasy 对应的结果:

curl -H "If-Modified-Since: Fri, 02 Oct 2020 08:23:16 GMT" \
     -I localhost:8080/evaluatePreconditions
HTTP/1.1 200 OK
Last-Modified: Fri, 02 Oct 2020 08:23:16 GMT
...

这种行为已经在 Resteasy 项目中提出,但是对于团队来说,修剪日期会添加一个新错误,因为如果在同一秒内多次修改数据/资源,我们会得到一个304如果我们修剪日期并且200如果我们没有,这是一个公平的观点。但是,我可能错了,但根据我对RFC 7232的理解,如果可以在同一秒内发生多项修改,我们也应该依赖ETag,这意味着在 JAX-RS 规范中,我们应该使用evaluatePreconditions(Date lastModified, EntityTag eTag)反而。

那么根据 JAX-RS 规范关于这种特殊情况的正确行为是什么?

标签: javajerseyjax-rsresteasyquarkus

解决方案


我认为没有具体说明evaluatePreconditions方法是否应该减少一秒钟的时间。但是:以不同的精度比较两个时间戳是“不公平的”。您要么应该舍入更精确的那个,要么将精度截断为相同。特别是因为 RFC 7232 甚至提到了 HTTP 标头的“低”精度问题并提出了解决方案(ETag)。

我还发现了一个 SO question with solutions how to compare timestamps with different precision:Compare Date objects with different levels of precision


推荐阅读