jQuery ajax ifModified не учитывается при вызове для кэшированного ответа, когда должно быть 304

Я использую jQuery ajax для извлечения json из приложения ASP.Net MVC, и я не вижу ожидаемых результатов при выполнении вызовов ajax, где ifModified = true,

Я использую jQuery 1.9.1 и Chrome версии 26.0.1410.64 м

При использовании нового экземпляра браузера и создании начального запроса содержимого, которое уже кэшировано, никогда не выполняется обращение к серверу для проверки состояния кэша, даже если в конфигурации 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.

Эта неспособность проверять состояние кэшированного источника каждый раз, когда он используется, является большой проблемой, поскольку кэшированные данные могут устареть, но пользователь не узнает об этом, пока не выполнит последующий запрос в том же экземпляре браузера.

2 ответа

Решение

Здесь происходят две вещи:

  1. Хром кеширует слишком агрессивно.

  2. ifModified=true может не совсем то, что вы думаете.

Chrome всегда должен отправлять запрос на сервер для проверки правильности кэшированного ресурса, но иногда кажется, что это не так. Такое поведение часто расстраивает веб-разработчиков, которые быстро изменяют свои ресурсы во время разработки.

JavaScript может только наблюдать 304 ответы, когда скрипт, выдающий запрос, явно устанавливает заголовки кэша. JavaScript не может использовать информацию о дате кэширования, известную браузеру; любая информация о дате кэширования должна быть обнаружена или выбрана самим сценарием (возможно, путем чтения последних измененных заголовков предыдущих ответов). фактический 304 Ответы, данные браузеру, будут сообщаться в JavaScript как 200 ответы, если JavaScript не установлен If-Modified-Since заголовок (или другие заголовки кэша).

Первая выборка для ресурса с jQuery никогда не вернется 304, Это потому, что jQuery не сохраняет значения для использования с If-Modified-Since заголовок между страницами загружается. Смотрите мой ответ на Как проверить, если статус заголовка запроса jQuery.ajax() "304 Не изменен"?:

Однако jQuery не сохраняет информацию в кэше (например, в файлах cookie) между перезагрузками страницы. Следовательно, первая выборка ресурса после перезагрузки страницы никогда не будет 304, потому что у jQuery нет информации для кэширования для отправки (т. Е. Мы возвращаемся к случаю "первой выборки"). Нет причин, по которым jQuery не может сохранять информацию в кэше, но в настоящее время это не так.

Причина в том, что 304 запроса всегда возвращаются пустыми. Предполагается, что ваш код хранит ответ в первый раз, когда он возвращается как 200, но jQuery не хочет навязывать вам требование сохранять кэшированный результат при перезагрузке страницы.

Я решил проблему поведения, отслеживая начальные запросы и добавляя 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;
    }
},
...

К сожалению, побочным эффектом является то, что ресурс всегда загружается при перезагрузке страницы независимо от состояния ее кэша.

Другие вопросы по тегам