Profruit banner

Умен звънец в openHAB

16 декември 2023

Телефонът е винаги с мен и сега, където и да се намирам получавам известие от openHAB, че някой е позвънил и чака пред вратата. А ако ме няма вкъщи става ясно часа и датата на последното позвъняване.

Tasmota Multipress

30 септември 2023

Идеята е Sonoff Touch T1 US 2 в салона, да управлява освен своето осветление и това в кухнята, а още вентилатора.

DIY 12V 1A WiFi Router UPS

22 април 2023

При поредно прекъсване на захранването вкъщи, батерията на нoтбука и двата UPS автоматично превключват на аварийно захранване и едновременно полита съобщение за конфуза

Zigbee2MQTT клониране

21 януари 2023

... как да клонираме съществуваща настройка на Zigbee2MQTT без да се налага последвало интервю на zigbee-устройствата.

LD2410 - бюджетен датчик присъствие в openHAB

11 февруари 2023

Цената на HLK-LD2410 зададе име на поредната тема в моя блог. С негова помощ се постига "народен" датчик присъствие в домашната автоматизация. . ...


Снимката долу показва съдържанието на каталог ajax, което е тестови сайт. Идеята е да се научим как да забраним на браузъра, да кешира нашите заявки и това е главния акцент. Съдържанието на кода не е обект на темата, а помощно средство за онагледяване.

test site

Главния проблем с когото всеки уеб-мастър се сблъсква е, че браузърите обожават да кешират отговорите на сървъра. Това е голям подводен камък, особено в AJAX и най-вече в IE-браузър. Всички браузъри кешират, дори прокси-сървърите, но при IE-браузър кеширането е съпоставимо с болест при заявка под метод GET.

метод GET и PHP


Хайде да спретнем малка демонстрация. Примитивен сървър, където PHP-сценарий връща тъпо времето. Досещате се, че това е файл gettime.php.
<?php
/*
 *  Сценарий, показващ текущото време
 */
//header("Content-type: text/plain; charset=utf-8");
//header("Current time: ". date("r"));
echo date('H:i:s');

По ред на браузърите правим следните действия.
  1. Заявка към файл gettime.php;
  2. Крачка назад в историята;
  3. Връщане обратно;
  4. Презареждане - F5;
Първи е Chromium браузър.

test site

Какво става на практика?
Заявка към файл gettime.php, печати в браузъра текущото време. Крачка назад в историята (оранжевата стрелка, сочеща наляво) и последвало връщане в текущия прозорец ни показва същото време. Няма изменение. Ако презаредим страницата - получаваме ново време, но ако пак прегледаме историята, то не се променя. И така с браузър Opera, Firefox и дори IE-9. Всички кешират!

Нека прихванем заглавките. А най-интересните са:
http://localhost/ajax/gettime.php

GET /ajax/gettime.php HTTP/1.1
Host: localhost
...

HTTP/1.1 200 OK
Date: Sat, 08 Jun 2013 17:50:33 GMT
Server: Apache/2.2.14 (Ubuntu)
X-Powered-By: PHP/5.4.15-1~lucid+1
...
Content-Type: text/html
Видно е, че метода е GET по протокол HTTP/1.1. Става ясно, че браузъра се обръща към PHP сценария, получава текущото време и до тук. То не се мени до нова заявка, а заигравката с историята е показателна, че отговора е кеширан.

метод GET и AJAX


Отлично. Нека усложним задачата и направим нещата под AJAX, за да проследим поведението на изброените браузъри.

xmlhttprequest.js е функцията, която връща обект XMLHttpRequest.
/*
** Функцията връща обект XMLHttpRequest
*/
function getXmlHttpRequest()
{
 if (window.XMLHttpRequest) 
 {
  try 
  {
   return new XMLHttpRequest();
  } 
  catch (e){}
 } 
 else if (window.ActiveXObject) 
 {
  try 
  {
   return new ActiveXObject('Msxml2.XMLHTTP');
  } catch (e){}
  try 
  {
   return new ActiveXObject('Microsoft.XMLHTTP');
  } 
  catch (e){}
 }
 return null;
Тази функция ще зацепим в индекс файла - index.html. Ето и него за финална сглобка на сайта.
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title>кеширане на заявка под метод GET</title>
 <script type="text/javascript" src="xmlhttprequest.js"></script>
 <script type="text/javascript">
  // заявка на данни
  function showServerTime()
  {
   var req = getXmlHttpRequest();
   req.onreadystatechange = function()
    {
     if (req.readyState != 4) return;
     var time = document.getElementById("time");
     time.firstChild.nodeValue = req.responseText;
    }
   req.open("GET", "gettime.php", true);
   req.send(null);
  }
  
  // автостарт
  window.onload = function()
   {
    setInterval("showServerTime()", 1000);
   }
 </script>
</head>
<body>
 <p>часовник <span id="time"> </span></p>
</body>
</html>
еквивалент на showServerTime()
function showServerTime(){
 var req = getXmlHttpRequest();

 req.open("GET", "gettime.php", true);
 req.onreadystatechange = function(){
  if (req.readyState === XMLHttpRequest.DONE && req.status === 200){
   var time = document.getElementById("time");
     
   time.innerHTML = req.responseText;
  }
 }
     
 req.send();
}
И разбира се правим заявка към индекса. На което Firefox 20.0, Chromium 25.0.1364.160, Opera 9.80 - linux версии честно показват часовника. Той тик-так-а. Отлично на функция setInterval и можем да отдъхнем, че браузърите са забравили да кешират. Тоест, отново чрез javascript се обръщаме към сървъра по метод GET, асинхронно. Получаваме времето и го пъхаме между таг-ове span на страницата.

browsers

Нека продължим. Откриваме Explorer9 9.0.5 под Windows 7 64-bit бла бла бла и хоп, не работи. Замръзване!

Explorer9

Към спънатия браузър се добави някаква стара Windows-версия на Opera. По-важното е, че може да ви се пръсне главата от мислене, защо в някои браузъри часовникът не работи. Скриптът е изряден и проблема остава в кеширането.

Смело може да се каже, че Windows-версия на Opera и Explorer9 честно са кеширали отговора и повече не се интересуват от измененията настъпили на сървъра. Тъпо се обръщат към кеша и четат значението съхранено там. Firefox в това отношение работи по-интелигентно и там кеширането става по желание на програмиста. Как ние можем да фиксираме това?

PHP забрана на кеширане


За съжаление много уеб-майстори не умеят това и тъпо преписват код, когото смело може да се нарече лайнокод. Същите дори не си правят труда, да видят ефективността на кода. Забранява ли въобще кеширането. Връх на невежеството е следния код:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); //Дата в миналото
header("Last-Modified: ".gmdate("D, d M Y H:i:s")."GMT"); 
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 
header("Pragma: no-cache"); // HTTP/1.1 
Най-забавното е, че този код не работи. Добавяме го в gettime.php и рестартираме Explorer9. Никаква промяна с часовника, замръзва както преди.

Заглавка Expires по идея трябва да определи дата на актуалността на документа. Но защо всички тъпо преписват Mon, 26 Jul 1997 05:00:00 GMT? Наберете тази дата като ключова фраза в търсачката на Google и ще понесете огромна доза потрес, на колко сайта се намира тя.

Този код е наследство от хелпа на PHP версия 3 и неговият автор в ден преди приведената дата е търсил начин как да забрани кеширането от браузъра. Той е смятал, че като върне един ден назад в миналото това ще работи. Днес сме 2013г, а PHP е 5.4 версия. Така че това е тъпня. Смело може да прочетем глава 13 на RFC2616, за да разберем замисъла на кеширането и Expires. С думи прости отсичаме тази заглавка.

Заглавка Last-Modified показва датата на изменение на документа, считайки че браузърите могат да четат това. Вероятно през 1997г това е било нормално, но днес това е загубило своето значение и смело може да се заяви, че спъвате работата на паяците и после има да се чудите защо не ви обичат. Отсичаме я.

Заглавка Pragma: no-cache. Наполеон, когато отстъпвал от опожарена Москва, този метод вече бил остарял. Този код е ерата на HTTP/1.0 и на днешен ден на всички браузъри им е през фара. В киреча!

Остава една заглавка - Cache-Control. И тя действително контролира кеширането. За съжаление тя има около десетина възможни значения. Сега ще я приведем в боен вид и ще компенсираме Last-Modified.

Аплодисменти за gettime.php и елегантния му вид.
<?php
/*
 *  Сценарий, показващ текущото време
 */

header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1 
echo date('H:i:s');

max-age и Expires е едно и също (поглед в снифера на заглавки). Най-главното е no-store и no-cache. no-store означава лични данни да не съхранява на локалния диск и той отсича кеширането в браузъра, а no-cache командва прокси-сървърите.

Рестартираме Explorer9 и о чудеса, часовникът тик-так-а!

Apache забрана на кеширането


Забраната за кеширане от браузъра може да се възложи на Apache. При всяка заявка той ще изпраща тази заглавки и по този начин няма да се чудите как да го постигате из документите на сайта ви. Досещате се че дойде ред на файл .htaccess от първата снимка горе.
# compiled modules enabled in apache on Debian
# user@machine:~$ /usr/sbin/apache2 -l
# <?php echo `/usr/sbin/apache2 -l`;
###############################################

# loaded modules enabled in apache on Debian
# user@machine:~$ ls /etc/apache2/mods-enabled/
###############################################

# list of disabled modules in apache on Debian
# ls /etc/apache2/mods-available/
###############################################

# enable mod_headers.c on Debian
# sudo a2enmod headers
# sudo /etc/init.d/apache2 reload
###############################################

# enable mod_expires.c on Debian
# sudo a2enmod expires
# sudo /etc/init.d/apache2 reload
###############################################

# header Cache-Control
###############################################
<ifModule mod_headers.c>
 Header set Cache-Control "no-store, no-cache"
</ifModule>

# header Expires
###############################################
<ifModule mod_expires.c>
 ExpiresActive On
 ExpiresDefault "now"
</ifModule>
Коректната работа на приведения .htaccess-файл за Debian базирани системи изисква включени модули mod_expires.c и mod_headers.c. Как се добавят става ясно от коментарите, които съм добавил в началото на файла.






до нови срещи   ^.^
08.06.2013 profruit 

|

0 Response to "Забрана кеширане от браузъра"

Публикуване на коментар

Този блог е реинкарнация на първите ми опити за споделяне в нета. На времето започнах с къси разкази на преживяното. После се обезсмисли и превърнах блога си в системно радио. Пиша единствено неща, които карат душата ми да живее: openHAB, Ubuntu, Споделено и т.н. Това е моето системно радио, разбирате ли? Моята вълна и вие сте на нея сега.

Архив на блога