3

我正在使用 jQuery ajax 从 ASP.Net MVC 应用程序中检索 json,并且在进行 ajax 调用时我没有看到预期的结果ifModified = true .

我正在使用 jQuery 1.9.1 和 Chrome 版本 26.0.1410.64 m

当使用新的浏览器实例并对已缓存的内容发出初始请求时,即使在 ajax 配置中设置了 ifModified=true,也不会调用服务器来检查缓存状态。在来自同一浏览器实例的后续请求中,(预期)调用服务器,服务器以 304 响应。

在初始请求中,浏览器网络跟踪显示内容是从缓存中传递的,状态为 200,即使从未检查过修改日期。Fiddler 显示未发出请求,并且未命中 ASP.Net MVC 控制器中的断点。

这是一个错误,还是有办法让它正常工作?如果是错误,是 jQuery 错误还是 Chrome 错误?

这是一个简化的 ajax 调用:

var req = $.ajax({
    type: "GET",
    dataType: "json",
    url: "/JsonTest/GetJson",
    ifModified: true,
    success: function (data, status, jqXHR) {
        if (data === undefined && status == "notmodified") {
            $.ajax({
                type: this.type,
                dataType: this.dataType,
                url: this.url,
                ifModified: false,
                success: this.success,
                error: this.error,
                complete: this.complete
            });
        } else {
            alert($.toJSON(data));
        }
    },
    error: function (jqXHR, status, error) {
        alert(error);
    },
    complete: function (jqXHR, status) {
    }
});

这是一个简化的 ASP.Net MVC 控制器方法:

[AllowAnonymous]
public ActionResult GetJson()
{
    // dummy data version.
    var currentVersion = new DateTime(2013, 04, 30, 12, 0, 0);

    // get client cached version from request
    DateTime cachedVersion;
    if (DateTime.TryParseExact(HttpContext.Request.Headers["If-Modified-Since"], "r", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out cachedVersion))
    {
        cachedVersion = cachedVersion.ToLocalTime();
    }
    else
    {
        cachedVersion = DateTime.MinValue;
    }

    // return 304 if not changed
    if (currentVersion.Subtract(cachedVersion).Duration() < TimeSpan.FromSeconds(1))
    {
        HttpContext.Response.StatusCode = 304;
        HttpContext.Response.StatusDescription = "Not Modified";
        return new EmptyResult();
    }

    // still here? return current object and set current version response header
    var rv = new { name = "Hello", value = "World" };
    HttpContext.Response.Cache.SetCacheability(HttpCacheability.Private);
    HttpContext.Response.Cache.SetLastModified(currentVersion);
    return Json(rv, JsonRequestBehavior.AllowGet);
}

重现步骤:1)启动新的浏览器实例 2)启动 ajax 请求 3)关闭响应警报窗口 4)启动 ajax 请求 5)关闭浏览器重复

第一次通过,清空缓存:第 2 步向服务器发出请求。服务器响应 200。步骤 4 向服务器发出请求,服务器响应 304。

第二次通过 - 缓存中的响应:步骤 2 不向服务器发出请求。响应来自状态为 200 的缓存。步骤 4 向服务器发出请求,服务器响应 304。

我希望第二次通过第 2 步向服务器发出请求并收到 304。

每次使用缓存源时都无法检查其状态是一个大问题,因为缓存数据可能是陈旧的,但用户在同一浏览器实例中发出后续请求之前不会知道它。

4

2 回答 2

7

这里发生了两件事:

  1. Chrome 缓存过于激进。

  2. ifModified=true可能不会你认为的那样做。

Chrome 应该始终向服务器发送请求以验证缓存资源的有效性,但似乎有时它根本没有。这种行为经常让 Web 开发人员在开发过程中迅速改变他们的资源感到沮丧。

JavaScript 只能304在发出请求的脚本显式设置缓存标头时观察响应。JavaScript 不能利用浏览器已知的缓存日期信息;任何缓存日期信息都必须由脚本本身发现或选择(可能通过读取先前响应的最后修改的标头)。304给浏览器的实际响应将作为200响应报告给 JavaScript,除非 JavaScript 设置了If-Modified-Since标头(或其他缓存标头)。

使用 jQuery 获取资源的第一次将永远不会返回304。这是因为 jQuery 不会If-Modified-Since在页面加载之间保存与标题一起使用的值。请参阅我关于如何检查 jQuery.ajax() 请求标头状态是否为“304 未修改”的答案?:

然而,jQuery 不会在页面重新加载之间保留缓存信息(例如,在 cookie 中)。因此,页面重新加载后第一次获取资源永远不会是 304,因为 jQuery 没有要发送的缓存信息(即,我们重置回“第一次获取”情况)。jQuery 没有理由不能持久化缓存信息,但目前它没有。

原因是 304 请求总是空着回来。假设您的代码在第一次返回时将响应存储为 200,但 jQuery 不想强加要求您在页面重新加载时保存缓存的结果。

于 2013-04-30T19:53:49.940 回答
2

我通过跟踪初始请求并将 beforeSend 添加到 ajax 配置解决了行为问题:

window.resourceInitializers = {};
...
beforeSend: function (jqXHR, settings) {
    if (!window.resourceInitializers['GetJson']) {
        jqXHR.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 2000 00:00:01 GMT")
        window.resourceInitializers['GetJson'] = true;
    }
},
...

不幸的副作用是资源总是在页面重新加载时检索,而不管其缓存状态如何。

于 2013-05-01T13:22:31.667 回答