Ajax - XMLHttpRequest
V predchádzajúcom dieli bolo vymenovaných niekoľko možných spôsobov komunikácie medzi klientom a serverom. Dnes sa zameriame na jeden z nich a ukážeme si, ako odosielať požiadavky a spracovať odpovede prostredníctvom objektu XMLHttpRequest.
Spôsobov, ako implementovať komunikáciu klienta so serverom je viac, avšak použitie objektu
XMLHttpRequest
sa s technikou Ajax spája asi najčastejšie. Použitie tohto objektu je v podstate jediné čisté riešenie komunikácie, pri ktorom sa technológia používa presne na to, na čo bola určená.
XMLHttpRequest
umožňuje vytvárať požiadavky s použitím rôznych metód, vrátane metód GET a POST. V dnešnej dobe ho podporujú všetky súčasné prehliadače.
Objekt
XMLHttpRequest
umožňuje prenášať údaje vo formáte XML, vďaka čomu môžeme na strane klienta jednoduchšie spracovávať prijaté údaje. Rovnako je však možné prenášať čistý text, čo sa hodí v prípadoch, v ktorých by bol XML formát kanónom na vrabce. Hoci je
XMLHttpRequest
určený na komunikáciu klienta so serverom, nie vždy je to tá najlepšia voľba. Nedostatkom objektu
XMLHttpRequest
je, že v prehliadači Internet Explorer je možné tento objekt vytvoriť iba s použitím technológie ActiveX. To môže byť problémom, pretože pri vyššom stupni zabezpečenia sa pri vytváraní tohto objektu môže používateľovi zobraziť otravná hláška, prípadne sa nemusí objekt
XMLHttpRequest
vôbec vytvoriť. V prehliadači Internet Explorer 7 už toto obmedzenie neplatí a objekt
XMLHttpRequest
sa dá vytvoriť rovnakým spôsobom ako v ostatných prehliadačoch.
Ďalšou vlastnosťou komunikácie prostredníctvom objektu
XMLHttpRequest
je, že nie je možné komunikovať so serverom z inej domény, než z ktorej bol spustený skript. Toto obmedzenie sa dá obísť nastavením prehliadača, ale z bezpečnostného hľadiska to nie je príliš rozumné. Ak by totiž bola povolená komunikácia so servermi z inej domény, bolo by jednoduché okrem iného napríklad hlasovať za nejakú možnosť v ľubovoľnej ankete, prípadne z Vašej IP adresy vložiť príspevok do nejakej diskusie na inej stránke. Pre komunikáciu so servermi z inej domény je preto lepšie vytvoriť na serveri proxy stránku, ktorá takúto komunikáciu zabezpečí.
Vytvorenie objektu XMLHttpRequest
Pri vytváraní objektu
XMLHttpRequest
sa postupne skúšajú viaceré spôsoby. Uprednostňuje sa vytvorenie objektu
XMLHttpRequest
jeho konštruktorom, ako keby to bol objekt ako každý iný. Až v prípade, že konštruktor nie je k dispozícii, príde na rad vytvorenie tohto objektu pomocou technológie ActiveX. V mnohých ukážkach na webe bolo toto poradie opačné a v podstate na ňom do istej doby ani nezáležalo. Ak bola k dispozícii technológia ActiveX, vytvoril sa
XMLHttpRequest
pomocou nej a až keď sa to nepodarilo, použil sa konštruktor pre
XMLHttpRequest
. Príchodom prehliadača Internet Explorer 7 už na poradí záleží, pretože ten umožňuje vytvoriť
XMLHttpRequest
obidvomi spôsobmi, pričom chceme uprednostniť natívny objekt
XMLHttpRequest
.
function createXhr ()
{
var xhr = null;
if (window.XMLHttpRequest)
xhr = new XMLHttpRequest ();
else if (window.ActiveXObject)
try { xhr = new ActiveXObject ("Msxml2.XMLHTTP"); }
catch (e)
{
try { xhr = new ActiveXObject ("Microsoft.XMLHTTP"); }
catch (e) {}
}
return xhr;
}
Možno sa pýtate, prečo sa pokúšame vytvoriť ActiveX objekt
Msxml2.XMLHTTP
a pri neúspechu vyskúšame ActiveX objekt
Microsoft.XMLHTTP
. Je to tým, že objekt
XMLHttpRequest
má viacero implementácií. Pomocou objektu
Microsoft.XMLHTTP
bolo možné vytvoriť objekt na komunikáciu so serverom dávno pred tým, než sa objavil objekt
XMLHttpRequest
v iných prehliadačoch. Neskôr vznikla ďalšia verzia tohto objektu
Msxml2.XMLHTTP
, ktorá sa líši snáď iba pomenovaním.
Vytvorený objekt
XMLHttpRequest
poskytuje niekoľko metód:
abort ():
Ukončí požiadavku.
getAllResponseHeaders ():
Vráti v reťazci zoznam všetkých hlavičiek.
open ("metóda", "url"[, asyncFlag[, "userName"[, "password"]]]):
Priradí požiadavke cieľovú URL, metódu a ďalšie voliteľné parametre.
send (content):
Odošle požiadavku so zadaným obsahom.
setRequestHeader ("label", "value"):
Pridá k požiadavke hlavičku.
Vlastnosti objektu XMLHttpRequest:
onreadystatechange:
Funkcia pre obsluhu udalosti zmeny stavu objektu.
readyState:
Číslo reprezentujúce aktuálny stav objektu: 0 = nenainicializovaný (uninitialized), 1 = nahráva (loading), 2 = nahraný (loaded), 3 = interaktívny (interactive), 4 = dokončený (complete)
responseText:
Reťazec s údajmi, ktoré poslal server.
responseXML:
XML (DOM-kompatibilný objekt) s údajmi, ktoré poslal server.
status:
Návratový kód odpovede, ako napr. 404 pre nenájdenú stránku alebo 200 pre úspech.
statusText:
Text, ktorý dopĺňa návratový kód odpovede.
Odoslanie požiadavky
Z predchádzajúceho výpisu metód objektu
XMLHttpRequest
vidíme, že pri otváraní novej požiadavky môžeme nastaviť, či má byť volanie synchrónne alebo asynchrónne. Predvolená hodnota je
true
, takže bez uvedenia tretieho argumentu metódy
open
bude volanie asynchrónne. Ak by sme nastavili tretí argument na
false
, volanie by bolo synchrónne. To by znamenalo, že hneď po zavolaní metódy
send
by sme mohli pracovať s odpoveďou, ku ktorej by sme sa dostali prostredníctvom vlastností
responseText
alebo
responseXML
. Avšak pri synchrónnom volaní musí klient na odpoveď čakať a počas tohto čakania nemôže nič robiť. Preto je vždy lepšie použiť asynchrónne volanie (okrem veľmi výnimočných prípadov).
Odoslanie požiadavky metódou POST
Najčastejšie sa používajú metódy
open
a
send
. Odoslanie požiadavky metódou POST by vyzeralo nasledovne:
function sendRequest (data, url)
{
var xhr = createXhr ();
if (xhr)
{
xhr.open ("POST", url);
xhr.onreadystatechange = function () { if (receiveResponse (xhr)) xhr = null; };
xhr.setRequestHeader ("Content-Type", "application/x-www-form-urlencoded");
xhr.send (data);
}
return xhr;
}
Pri asynchrónnom volaní nastavíme obslužnú funkciu udalosti
readystatechange
, ktorá sa potom zavolá pri každej zmene stavu požiadavky. Tu by som chcel upozorniť na funkciu, ktorú sme v príklade použili ako obslužnú. Táto funkcia volá ďalšiu funkciu
receiveResponse
, ktorej v argumente posunieme objekt
xhr
. Dôležité je to, že po spracovaní odpovede, keď už viac nebude potrebné pristupovať k objektu
xhr
, vráti funkcia
receiveResponse
hodnotu
true
, aby bolo možné uvoľniť objekt
xhr
z pamäte. V mnohých ukážkach na webe takéto uvoľňovanie pamäte chýba, čo má za následok v niektorých prehliadačoch to, že pri každej požiadavke na server sa obsadí ďalší kus pamäte. Konkrétne ide o tzv. memory leaks v prehliadači Internet Explorer. Táto pamäť sa dá potom uvoľniť jedine reštartom prehliadača.
Pri požiadavkách odosielaných metódou POST musíme ešte nastaviť hlavičku požiadavky
Content-Type
na
application/x-www-form-urlencoded
, aby server vedel, že obsahom je zakódovaný formulár.
Odoslanie požiadavky metódou GET
Odoslanie požiadavky metódou GET je veľmi podobné, ale o niečo jednoduchšie. Keďže odosielané údaje sú zakódované už priamo v URL, netreba ich v metóde send nastavovať. Podobne netreba nastavovať hlavičku:
function sendRequest (data, url)
{
var xhr = createXhr ();
if (xhr)
{
xhr.open ("GET", url);
xhr.onreadystatechange = function () { if (receiveResponse (xhr)) xhr = null; };
xhr.send (null);
}
return xhr;
}
Nasledujúcim krokom bude prijatie a spracovanie odpovede nastavenou metódou
receiveResponse
.
Prijatie odpovede
Pri vytváraní požiadavky sme priradili objektu
XMLHttpRequest
obslužnú funkciu, ktorá sa volá pri výskyte udalosti
readystatechange
. Úlohou tejto obslužnej funkcie je spracovať prijatú odpoveď, prípadne ohlásiť chybu. Môže vyzerať nasledovne:
function receiveResponse (xhr)
{
if (xhr.readyState == 4)
{
if (!xhr.status || xhr.status == 200)
alert (xhr.responseText);
else
alert ("Error: " + xhr.statusText);
return true;
}
return false;
}
Pri prijatí odpovede sa v prvom rade skontroluje vlastnosť
readyState
. Tá obsahuje kód stavu, v akom sa požiadavka nachádza. V drvivej väčšine prípadov chceme obslúžiť stav s kódom 4, kedy sa dokončilo prijatie odpovede. V komunikácii cez protokol HTTP má každá odpoveď svoj stav, ktorý je vyjadrený číslom vo vlastnosti status. Ak prišla odpoveď v poriadku, bude jej stav označený číslom 200. Iný stav znamená, že pravdepodobne nastala nejaká chyba. Ak spúšťame skript z lokálneho počítača bez použitia webového servera, môžeme pristupovať priamo k súborom na disku. Keďže v takomto prípade nekomunikujeme s webovým serverom, nemusí byť stav odpovede nastavený.
Táto funkcia vráti
true
v prípade, že požiadavku spracovala a objekt
xhr
nebude už viac potrebovať. V opačnom prípade vráti
false
. Ako bolo skôr spomenuté, takéto správanie sme si definovali kvôli správnemu uvoľňovaniu pamäte.
Záver
V tejto časti seriálu sme si ukázali základy práce s objektom
XMLHttpRequest
, ktoré by mali stačiť na vytvorenie jednoduchej komunikácie klienta so serverom. V ďalšej časti si ukážeme, ako by sa dala realizovať ekvivalentná komunikácia prostredníctvom skrytého rámca.
Čím sa líši táto ukážka od ostatných príkladov komunikácie klienta so serverom dostupných na Internete je hlavne to, že sa staráme aj o uvoľňovanie pamäte. Keď nepoužívame takúto komunikáciu intenzívne, nemusíme sa o neuvoľnenú pamäť príliš starať. Pri častej komunikácii však bez uvoľňovania pamäte hrozí kolaps aplikácie, webového prehliadača, prípadne celého systému z dôvodu vyčerpania systémových zdrojov.
Dobré. Potíže s memory leaks mají dost prohlížeče sami o sobě. Třeba u mě je to znatelné už po větším počtu otevřených stránek . Musím po čase resetovat prohlížeč, aby mi to běhalo svižněji. Z tohoto důvodu je dost dobré to doplnění o "return false;
Jinak by se dost lidí asi divilo při různém, intenzivním využívání této funkce aplikací, kde se jim pořád ztrácí paměť.
Ale stejně si to ještě vyzkouším, protože při opakovaném použití funkce function sendRequest (data, url) je použito:
var xhr = createXhr (); Od čehož bych očekával, že zruší předchozí zabranou paměť respektive bych očekával, že objekt xhr nebude v paměti více než 1x.... nebo ano ?
Pri každom odoslaní požiadavky sa vo funkcii sendRequest vytvára nový objekt, ktorý sa zapamätá v novej premennej xhr. Tá sa po dokončení vykonávania funkcie sendRequest nemôže uvoľniť, pretože je použitá v anonymnej funkcii, ktorá obsluhuje udalosť readystatechange. Neuvoľní sa ani pri opustení stránky, pretože V IE vznikne kruhová referencia medzi ActiveX objektom a objektom JavaScriptu, s ktorou má Garbage Collector problém.
Na komunikáciu by sa mohol používať len jeden objekt XMLHttpRequest, ale treba pri tom dodržať nejaký postup (priradiť obslužnú funkciu pre readystatechange až po volaní open): http://keelypavan.blogspot.com/2006/03/reusing-xmlh ttprequest-object-in-ie.html.