jQuery ajax在调用缓存响应时未正确处理ifModified选项,应返回304状态码。

3
我正在使用jQuery ajax从ASP.Net MVC应用程序检索json,并且当进行ajax调用时,如果设置ifModified=true,则无法看到预期的结果。
我正在使用jQuery 1.9.1和Chrome Version 26.0.1410.64 m。
在使用新的浏览器实例并获取已缓存内容的初始请求时,即使在ajax配置中设置了ifModified=true,也不会向服务器发出检查缓存状态的调用。在来自同一浏览器实例的后续请求中,服务器将如预期地响应304。
在初始请求中,浏览器网络跟踪显示从缓存中传递了内容,状态为200,即使未检查修改日期。Fiddler未显示任何请求,而ASP.Net MVC控制器中的断点未被触发。
这是一个简化的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。

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


你尝试过将HttpContext.Request.Headers["If-Modified-Since"]记录到控制台中,以便查看它是否适当地更改了吗? - Dropzilla
jQueryеңЁйЎөйқўеҠ иҪҪд№Ӣй—ҙдёҚдҝқеӯҳеҖјд»ҘдёҺIf-Modified-SinceдёҖиө·дҪҝз”ЁгҖӮиҜ·еҸӮи§ҒеҰӮдҪ•жЈҖжҹҘjQuery.ajax()иҜ·жұӮеӨҙзҠ¶жҖҒжҳҜеҗҰдёәвҖң304 Not ModifiedвҖқ?гҖӮ - apsillers
2个回答

7
这里出现了两个问题:
  1. Chrome的缓存过于积极。

  2. ifModified=true可能并不像你想象的那样。

Chrome应该始终向服务器发送请求以验证缓存资源的有效性,但似乎有时它就是不这样做。这种行为经常会让Web开发人员感到沮丧,因为他们在开发过程中快速修改资源。
JavaScript只能观察当脚本发出请求时显式设置缓存标头的304响应。JavaScript不能利用浏览器已知的缓存日期信息。任何缓存日期信息都必须由脚本自身发现或选择(可能通过读取先前响应的最后修改标头)。实际上给浏览器的304响应将被报告给JavaScript作为200响应,除非JavaScript设置了If-Modified-Since标头(或其他缓存标头)。
jQuery首次获取资源时永远不会返回304。这是因为jQuery在页面加载之间不保存用于使用If-Modified-Since标题的值。请参阅我在如何检查jQuery.ajax()请求标题状态是否为“304 Not Modified”?上的答案:

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

这是因为304请求始终返回空。假定你的代码将响应作为200的第一次返回存储起来,但jQuery不希望强制要求你在页面重新加载时保存缓存结果。

你的另一篇帖子解释了为什么会发生这种情况,但在我看来,这种行为就是纯粹的愚蠢。在浏览器环境中请求资源应该是一致的,无论是HTML img元素、script元素还是通过JavaScript进行的ajax调用。 - Lee Greco
行为是一致的,但使用ajax时您可以控制请求的格式和时间。如果行为不同,则是因为请求也不同(例如,etag / last modified头的使用不同)。 - xentek

2

我通过记录初始请求并在ajax配置中添加beforeSend来解决了行为问题:

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;
    }
},
...

不幸的是,无论缓存状态如何,资源在页面重新加载时总是被检索。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接