В последние пару дней мне было очень весело отлаживать проблемы наших JavaScript SPAs на oldIE (слава Богу, мы получили лицензии Browserstack). В любом случае, я столкнулся с довольно интересным поведением IE при вызовах jQuery Ajax и кэшированных ответах.
«HTTP / 1.1 304 не изменен»: ни за что! Ты меня не поймаешь
Чтобы этот эксперимент работал, убедитесь, что ваш кеш браузера активен.
Перенесите следующий ajax-запрос в статический файл JSON
$.ajax({ url: '/data.json', type: 'GET', dataType: 'JSON', success: function(data, textStatus, jqXHR){ console.log('Data received: ' + data); } });
Первый запрос, вероятно, возвращается через некоторое Http/1.1 200 OK
время, а любой последующий (если на сервере активировано кэширование) возвращается с Http/1.1 304 Not Modified
. Вы хорошо видите это на панели Chrome Network
Теперь предположим, что вы хотите выполнить какую-то операцию только Http/1.1 200
над 304
запросами, а не над ними . Достаточно просто, правда? Вы просто сверяете с jqXHR.status
объектом:
... success: function(data, textStatus, jqXHR){ if(jqXHR.status === 200){ console.log('Got a good request'); } }
Интересно, что для вышеупомянутого запроса я бы получил
Got a good request Got a good request Got a good request
… Напечатано на моей консоли и, действительно, при проверке по запросу jqXHR jqXHR.status
возвращается как 200
для, Http/1.1 200
так и для Http/1.1 304
. Почему это?? Это дизайн объекта XMLHttpRequest .
Для 304 неизмененных ответов, которые являются результатом условного запроса, сгенерированного пользовательским агентом, пользовательский агент должен действовать так, как если бы сервер дал ответ 200 OK с соответствующим содержимым. […] W3c.org [RFC2616]
Подожди .. есть способ!
На самом деле есть способ также получить 304
коды состояния в вашем обратном вызове jQuery ajax. Очередной раз:
… Пользовательский агент должен разрешить setRequestHeader () переопределять автоматическую проверку кэша, устанавливая заголовки запроса (например, If-None-Match, If-Modified-Since), в этом случае должны передаваться ответы 304 Not Modified. w3c.org [RFC2616]
Но обратите внимание, у вас нет доступа к информации браузера If-Modified-Since
или E-Tag. Однако jQuery отслеживает это и позволяет использовать ifModified
флаг для параметров ajax:
$.ajax({ url: '/data.json', type: 'GET', dataType: 'JSON', ifModified:true, success: function(data, textStatus, jqXHR){ if(jqXHR.status === 200){ console.log('Got a good request'); } else { console.log('Hey, a 304 request'); } console.log('Data: ' + data); } });
Выполняя это сейчас снова, где первый запрос возвращает 200, а последующие 300 статус ответа дает мне следующее в журнале:
Got a good request Data: <snip>JSON string</snip> Hey, a 304 request Data: undefined Hey, a 304 request Data: undefined
Черт возьми ?? Да, опять это по замыслу:
[…] В этом случае 304 Неизмененные ответы должны быть пропущены через […]
… и действительно, 304
ответ не несет никаких данных.
Хорошо, но почему это должно быть проблемой?
Обычно это не так, просто не ставьте ifModified
флаг, и вы в порядке. Однако в моем конкретном сценарии мне пришлось анализировать изменения в определенном заголовке ответа, поэтому я использовал такую ajaxComplete(..)
функцию, как
var currentHeaderVal = undefined; $(document).ajaxComplete(function(e, xhr, settings){ var newHeaderVal = xhr.getResponseHeader('MY-HEADER'); if(currentHeaderVal !== newHeaderVal){ //do something fancy } });
На стороне сервера какой-то механизм ввел MY-HEADER
значение. Идея проста, и она также работает (на Chrome, Firefox и любом другом достойном браузере), но, к удивлению, на IE это не так. При использовании xhr.getResponseHeader('MY-HEADER')
IE возвращает заголовки из ранее кэшированного запроса, которые, очевидно, уже не обязательно действительны.
Таким образом, идея состояла в том, чтобы проверить xhr.status
, чтобы просто прочитать заголовки из 200
запросов типа, как
var newHeaderVal = xhr.getResponseHeader('MY-HEADER'); if(xhr && xhr.status === 200){ if(currentHeaderVal !== newHeaderVal){ //do something fancy } }
… но, как мы теперь знаем, это не сработает.