Как проверить, что заголовок запроса jQuery.ajax() имеет статус "304 Не изменен"?

Как проверить, что заголовок запроса jQuery.ajax() имеет статус "304 Не изменен"?

jqXHR.status обычно возвращается 200, даже если запрашиваемый заголовок равен "304 Not Modified".

ifModified:true не очень помогает, потому что это нарушает запрос данных XHR.

5 ответов

Решение

Без обработки заголовков кэша вручную это невозможно. Обычно 304 ответа не доступны через XHR API:

Для 304 неизмененных ответов, которые являются результатом условного запроса, сгенерированного пользовательским агентом, пользовательский агент должен действовать так, как если бы сервер дал ответ 200 OK с соответствующим содержимым.

Обычно jQuery не знает, что было 304 ответа, потому что браузер вежливо говорит ложь JavaScript о том, что на самом деле происходит в сети.

Но есть и хорошие новости (вроде): вы можете заставить Ajax выдавать ответ 304, но только путем ручной настройки заголовков кэша HTTP.If-Modified-Since или же If-None-Match в запросе:

Пользовательский агент должен разрешать setRequestHeader() отменить автоматическую проверку кэша, установив заголовки запроса (например, If-None-Match, If-Modified-Since), в этом случае 304 Неизмененные ответы должны быть переданы.

Итак, вы можете использовать код как:

var xhr = new XMLHttpRequest();
xhr.open("GET", "foo.html");
xhr.setRequestHeader("If-Modified-Since", "Fri, 15 Feb 2013 13:43:19 GMT");
xhr.send();

Одна из основных трудностей заключается в том, как узнать, какую дату последнего изменения или ETag отправлять? Браузер имеет информацию о кеше, которую он использует для отправки запросов, но он не будет делиться этой информацией с JavaScript. К счастью, jQuery отслеживает Last-Modified а также ETag Заголовки из ответов Ajax, так что вы можете использовать ifModified:true чтобы jQuery установил эти значения заголовка при следующей отправке запроса на этот ресурс.

Об этом следует отметить две вещи:

  • 304 ответа не содержат данных. Это по замыслу. Предполагается, что если вы решили использовать кэширование, у вас должна быть копия данных, уже находящихся в вашем кэше! Если получение данных с сервера не является проблемой (т. Е. Потому что у вас их еще нет), почему вы используете кеширование? Кэширование должно использоваться, когда у вас есть старые данные под рукой и вы хотите только новые данные; таким образом, возвращение данных без 304 не должно быть проблемой.

  • jQuery должен иметь дату последнего изменения или ETag (для использования с If-None-Match) сохранено из предыдущего запроса. Процесс идет так:

    • Первая выборка: у jQuery нет информации о кеше, поэтому он не отправляет If-Modified-Since или же If-None-Match, Когда ответ возвращается, сервер может объявить последние измененные данные или ETag, который jQuery хранит для будущего использования.

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

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

Суть в том, что вы можете использовать заголовки кэша для получения ответа JavaScript 304, но вы не можете получить доступ к собственному ETag браузера или дате последнего изменения для определенного ресурса. Таким образом, сам браузер может знать информацию о кешировании ресурса, а ваш код JavaScript - нет. В этом случае браузер будет использовать свои заголовки кэша, чтобы потенциально получить реальный ответ 304, но перенаправить ответ 200 на ваш код JavaScript, потому что JavaScript не отправлял никакой информации кэша.

Невозможно обеспечить полное соответствие запросов JavaScript 304 фактическим ответам сети 304, поскольку информация о кеше, известная вашему браузеру, и информация о кеше, известная вашему коду JavaScript, могут отличаться непредсказуемым образом. Тем не менее, правильное получение 304 запросов в большинстве случаев достаточно для большинства практических задач разработки.

пример

Вот краткий пример сервера, написанный на Node.js (но он должен быть достаточно простым для переноса на другие языки):

require("http").createServer(function (req, res) {
  console.log(req.headers["if-modified-since"]);

  // always send Last-Modifed header
  var lastModDate = "Fri, 13 Feb 2013 13:43:19 GMT";
  res.setHeader("Last-Modified", lastModDate);

  // if the request has a If-Modified-Since header,
  //   and it's newer than the last modification,
  //   then send a 304 response; otherwise send 200
  if(req.headers["if-modified-since"] &&
     Date.parse(lastModDate) <= Date.parse(req.headers["if-modified-since"])) {
    console.log("304 -- browser has it cached");
    res.writeHead(304, {'Content-Type': 'text/plain'});
    res.end();
  } else {
    console.log("200 -- browser needs it fresh");
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('some content');
  }

}).listen(8080);

При запуске этого сервера вы можете загрузить страницу в браузере и выполнить два разных теста в консоли браузера:

var xhr = new XMLHttpRequest();
xhr.open("GET", "/");
xhr.send();
xhr.onload = function() { console.log(xhr.status); }

Этот скрипт всегда будет видеть 200 ответ, даже если браузер предоставляет If-Modified-Since заголовок запроса и получает 304 (что произойдет все запросы после первого, после того, как браузер видит сервер Last-Modifed заголовок ответа).

В отличие от этого сценарий всегда будет видеть ответ 304:

var xhr = new XMLHttpRequest();
xhr.open("GET", "/");
xhr.setRequestHeader("If-Modified-Since", "Fri, 15 Feb 2013 13:43:19 GMT");
xhr.send();
xhr.onload = function() { console.log(xhr.status); }

Сценарий предоставляет свой собственный If-Modified-Since заголовок запроса (через два дня после даты последнего изменения сервера); он не зависит от того, что браузер поставляет для If-Modified-Sinceи, следовательно, разрешено (согласно спецификации XHR) видеть 304 ответа.

Наконец, этот скрипт всегда будет видеть 200:

var xhr = new XMLHttpRequest();
xhr.open("GET", "/");
xhr.setRequestHeader("If-Modified-Since", "Fri, 12 Feb 2013 13:43:19 GMT");
xhr.send();
xhr.onload = function() { console.log(xhr.status); }

Это потому, что скрипт использует If-Modified-Since до даты последнего изменения сервера, поэтому сервер всегда отправляет 200, Сервер не отправит 304 поскольку предполагается, что у клиента нет кэшированной копии самой последней версии (т. е. клиент объявляет, что он видел изменения с 12 февраля, но было изменение 13 февраля, которого клиент, очевидно, не видел).

Также заголовки будут кэшироваться, если ответ приходит из кэша. Я принял это как шанс. Мой сервер отправляет уникальный заголовок md5. Это не имеет такого же эффекта, как? Query=time(). Теперь на стороне JS проверьте последний заголовок md5 с текущим. Если вы получаете тот же заголовок MD5, что и раньше, ответ приходит из кеша. Я забыл все о JQuery, но увидел, что можно устанавливать и читать заголовки.

<?php
$oldMD5=filter_input(INPUT_SERVER,'HTTP_CONTENT_MD5',int);
if(!empty($oldMD5)){
  header('Content-MD5: '.(1+(int)$oldMD5));
}
--------------------------------------------------------------
<script>
var oldMD5=0;
//your Ajax implementation 
Ajax.setRequestHeader('Content-MD5',''+oldMD5);
var newMD5=parseInt(Ajax.getResponseHeader('Content-MD5'),10);
if(newMD5 && oldMD5<newMD5,10){
  oldMD5=newMD5;
  //not cached
}else{
  //cached
}

Это немного злоупотребляет, потому что md5 должен быть хешем для проверки того, что "декодированные данные - это те же данные, которые были первоначально отправлены".

У меня есть вид реализации в https://github.com/laukstein/ajax-seo/blob/cbed03222d89f6f7e75dc49ce8882c30201bbb86/index.php

var cache;

$.ajax({
    ...
    beforeSend: function() {
        cache = setTimeout(function() {
            console.log('The content not cached jet.');
        }, 300);
    },
    success: function(data) {
        if (cache) {
            clearTimeout(cache);
        }
    }
});

Может попробовать это?

$.ajax({
    url: 'your-url',
    dataType: 'script',
    complete: function(xhr) { 
        if (xhr.status == 304) return;
        // or better: if (xhr.status != 200) return;
        // your code goes here
    }
});

Откройте инспектор браузеров, и он покажет вам информацию о запросе ajax, включая статус.

для Chrome щелкните правой кнопкой мыши и выберите элемент Inspect, затем перейдите на вкладку Network.

для firefox просто используйте firebug и выполните те же шаги.

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