Gradnik za dinamično sodelovanje

Objavljeno: 25.3.2005 01:44 | Avtor: Uroš Mesojedec | Kategorija: Programer | Revija: Marec 2005

Vse bolj znani gradnik XML HTTP Request bo najverjetneje spremenil uporabniško izkušnjo številnih spletnih strani. Oglejmo si njegovo rabo.

Že vse od nastanka spletnih strani so njihovi avtorji iskali način, kako bi jih naredili dinamične in s tem privlačnejše. Danes imamo v spletu že cel kup storitev, nekatere izmed njih so naravnost fantastične, vendar se velika večina še vedno uporablja po načelu: naloži stran, izberi, naloži znova. Tako spletni razvijalci že leta preizkušajo najrazličnejše načine vzdrževanja čim manj očitne povezave s strežnikom, ki bi jim omogočila bolj gladko in neopazno obnavljanje vsebine spletne strani. S tem se obdelava podatkov, glede na uporabniške želje, lahko neopazno odvije v ozadju, najpogosteje na veliko zmogljivejši infrastrukturi, izid pa se posreduje na način, ki ne zahteva vnovičnega nalaganja strani; to namreč poleg občutka počasnejšega delovanja in nepotrebnega prenašanja celotne strani najpogosteje prinese še druge nevšečnosti, kot sta opazno utripanje (flickering) zaradi vnovičnega nalaganja celote in izguba trenutnega položaja na globokih straneh (scroll position).

V zadnjih letih je vse več govor o spletnih storitvah in univerzalnem označevalnem jeziku, XML, ki omogočata nove načine medsebojnega sodelovanja različnih računalniških sistemov. Tehniko spletnih storitev lahko neposredno ali prirejeno uporabimo tudi za odpravo prej navedenih težav, če bi bil na voljo način, kako se odjemalec lahko pogovarja s strežnikom tudi v "ozadju", brez potrebe po naslavljanju drugega spletnega naslova, kar povzroči vnovično nalaganje vsebine s strežnika. Na srečo je to na voljo in se je v minulem letu kot požar razširilo po praktično vseh bolj priljubljenih spletnih brskalnikih.

Brskalniki

Prvi je gradnik, ki ustreza našim željam, predstavil Microsoft [1]. V sklopu paketa za podporo spletnim storitvam je ponudil nekaj gradnikov COM/ActiveX, ki prinašajo možnost nalaganja vsebine s poljubnih spletnih naslovov in njihovo členjenje v predmetni model DOM. Ko so razvijalci v projektu Mozilla še lovili korak z Internet Explorerjem, so spoznali tudi prednosti omenjenega gradnika, zato so se odločili, da v svojo kodo vgradijo funkcijsko enakovreden ekvivalent. Ker gre za najbolj priljubljena spletna brskalnika, so podporo gradniku kmalu prinesli tudi drugi spletni brskalniki. Applov Safari ga je podprl z različico 1.2. Opera ga prepozna od različice 7.6, kjer pa je podpora še nekoliko hroščata, bolje je v osmici. Sledil je tudi Konqueror, ki pa za uporabno delovanje zahteva namestitev aktualnih popravkov.

Zanimivo, da je funkcionalnost, podobna storitvam gradnika, opisana tudi v predlogu standarda organizacije W3C, Document Object Model Level 3 Load and Save Specification [2]. Vsekakor se je gradnik zdaj že toliko "prijel", da bo njegova uporaba ostala taka, kot jo bomo spoznali v nadaljevanju. Bo pa v prihodnosti verjetno na voljo še en, "standardiziran" način vzpostavljanja povezave s spletnimi viri v ozadju.

Vzpostavitev gradnika

Kot smo spoznali, je ledino oral Microsoft. S tem je postavil tudi zgled za poimenovanje gradnika, ki ga najpogosteje imenujemo XML HTTP Request, čeprav lahko z njim poizvedujemo o vseh vsebinah, ki so na voljo v spletu, ne le za zapis XML. Res pa je, da je struktura XML (kar lahko zajema tudi ustrezno urejen HTML oziroma, natančneje, XHTML) zelo udobna za nadaljnje členjenje v javascriptu, kar bi za npr. binarni zapis pretočnega videa le stežka trdili.

V brskalniku Internet Explorer lahko gradnik vzpostavimo z lastniškim konstruktorjem Microsofta, ActiveXObject(). Na voljo imamo kar dve različici gradnika, odvisno od različice paketa MSXML [1], ki je nameščen v našem sistemu. Ker je novejši učinkovitejši, kodo za vzpostavitev najpogosteje zapišemo tako:

var XH = null;

try {

XH = new ActiveXObject("Msxml2.XMLHTTP");

} catch(e1) {

try {

XH = new ActiveXObject("Microsoft.XMLHTTP");

} catch(e2) {

XH = null;

}

}

Seveda je možno, da se naša koda ne bo izvajala v ustreznem brskalniku in bo predmet XH še vedno nevzpostavljen. Zato v nadaljevanju poskrbimo še za druge brskalnike, ki zaenkrat spoštujejo poimenovanje razreda, kot ga je predstavila Mozilla:

if (!XH && typeof XMLHttpRequest != "undefined") {

XH = new XMLHttpRequest();

}

Če je ustrezen gradnik podprt, bomo imeli izvod njegovega razreda zdaj pripravljen v predmetni spremenljivki XH.

Zmožnosti

Gradnik podpira več metod in lastnosti. Vse spodaj naštete so trenutno na voljo v brskalnikih IE, Mozilla (in sorodnikih) in Safari 1.2, najpomembnejše pa tudi v Operi in Konquererju.

Metode gradnika

Pri delu z gradnikom najpogosteje uporabljamo metodi open() in send(), ki sta tudi najširše podprti. V metodi open() v prvem parametru podamo metodo zahteve. Tipično uporabljamo niz GET, kadar nas zanima vsebina na določenem naslovu, oz. niz POST, kadar strežniku za ustrezen odgovor posredujemo še dodatno vsebino. Pri metodi GET lahko dodatne parametre zahtevka opišemo tudi kot del naslova URL; če njihova dolžina presega 512 bajtov ali pri strežnikih, ki pač zahtevajo uporabo metode POST, je drugi način zanesljivejši. Naslov URL je lahko relativen ali poln, vendar nam brskalnik najverjetneje ne bo dovolil dostopa do drugih spletnih naslovov, temveč se stran lahko sklicuje le nazaj, na strežnik, s katerega je prišla sama. Zato se najpogosteje uporabljajo kar relativni naslovi, ki se tako ali tako nanašajo na strežnik, s katerega je sama spletna stran.

Zaradi te dodatne, vendar povsem razumljive varnostne omejitve je uporaba povezovanja v ozadju seveda omejena. Z njo ne moremo kar poljubno povpraševati po vseh spletnih virih, ki so na voljo. No, delna rešitev, ki je verjetno uporabna le v nekaterih intranetnih okoljih, je znižanje varnostne zaščite v brskalniku Internet Explorer, ki v tem primeru ne bo več opozarjal o nevarnosti povezovanja na druge spletne naslove. Zanimivo, da nas IE ne opozarja, če skriptno kodo izvajamo iz datoteke HTML, ki smo jo v brskalniku odprli neposredno, brez posredovanja skozi spletni strežnik (prepoznamo jo po protokolu file://, ki ga najdemo v polju za vnos spletnega naslova). Povsem drugače je v brskalniku Mozilla. Ta pri neposrednem nalaganju datoteke HTML povezave sploh ne bo dopustil (torej deluje le, če je protokol v spletnem naslovu http://), povezavo na druge naslove pa bo dovolil le na podpisanih straneh, ki zahtevajo pravico UniversalBrowserRead. Več o tem si lahko preberemo na [3]. Vsekakor bo najpreprosteje, če se z gradnikom vračamo le nazaj, v strežnik, iz katerega stran izvira, torej uporabljamo relativne naslove URL.

Zelo pomemben parameter metode open() je asinhrono delovanje. Želimo ga postaviti na resnično vrednost (true), saj s tem ne bomo "blokirali" izvajanja celotne spletne strani. Seveda moramo v tem primeru poznati mehanizem, kako bomo obveščeni ob koncu zahtevka. Za to poskrbi ustrezna lastnost, ki jo bomo spoznali v nadaljevanju. Sledita še dva neobvezna parametra, ki ju lahko uporabimo za dostop do strani, ki zahtevajo prijavo z imenom in geslom.

Sama metoda open() še ne sproži zahtevka. Pred tem lahko nastavimo poljubne dodatne elemente glave z metodo setRequestHeader(), tipično pa nastavimo mehanizem za prestrezanje asinhrono podanega zahtevka. Zahtevek dokončno sprožimo z metodo send(), katere edini parameter je morebitna vsebina, ki jo želimo posredovati v zahtevi tipa POST. ČE je ni, pač posredujemo konstantno vrednost null.

Oglejmo si še lastnosti gradnika:

Lastnosti gradnika

Z opisom lastnosti smo spoznali vse potrebno za popolno vzpostavitev gradnika in obdelavo zahtevka. Ponazarja ga spodnja koda:

function vzpostavi() {

try {

XH = new ActiveXObject( "Msxml2.XMLHTTP" )

} catch( e1 ) {

try {

XH = new ActiveXObject( "Microsoft.XMLHTTP" )

} catch( e2 ) {

XH = null;

}

}

if ( !XH && typeof XMLHttpRequest != "undefined" ) {

XH = new XMLHttpRequest()

}

}

function nalozi( url ) {

if ( XH ) {

try {

XH.open( "GET", url, true );

XH.onreadystatechange = dogodek;

XH.send( null );

} catch (e) {

alert("Napaka\n\n" + e);

}

}

}

function dogodek() {

if ( XH.readyState != 4 ) return;

alert(XH.responseText);

}

var XH = null;

vzpostavi();

nalozi("test.htm");

Po vzpostavitvi gradnika smo v funkciji nalozi() opredelili tudi funkcijo, ki jo gradnik pokliče v odzivu na spremembo stanja zahtevka. Ker smo pri opisu lastnosti spoznali, da je stanj zahtevka kar 5, moramo v funkciji ustrezno poskrbeti za reakcijo. Tokrat smo se odločili, da nas zanima le izpolnjeni zahtevek. V tem primeru skozi opozorilo (metodo alert()) prikažemo besedilo odgovora. Metodi nalozi() smo naročili, naj nam naloži relativni naslov URL, v zgornjem primeru je to kar ime datoteke, ki jo uporabljamo (torej naloži in prikaže lastno kodo).

Različna raba

Oglejmo si še nekaj zgledov rabe gradnika. Zelo zanimivo je poizvedovanje samo po glavi odgovora. V tem primeru lahko uporabimo metodo HEAD (ta žal v vseh brskalnikih (še) ni podprta, deluje pa v obeh prevladujočih).

Spodnji zgled nam prikaže vsebino celotne glave odgovora:

var XH = null;

vzpostavi();

XH.open("HEAD", "test.htm", true);

XH.onreadystatechange = function() {

if (XH.readyState == 4) {

alert(XH.getAllResponseHeaders())

}

}

XH.send(null);

V zgledu smo uporabili kar brezimno funkcijo, ki smo jo določili kot prestreznika ustreznega dogodka. Poskrbela je, da smo celotno glavo odgovora dobili prikazano skozi opozorilo (slika 1).

Slika 1: Glava odgovora, kot jo prikaže prestreznik dogodka onreadystatechange.

Lahko bi bili tudi bolj izbirčni. V glavi je med drugim zapisan datum zadnje spremembe datoteke, ki ga lahko izluščimo z ustreznim klicem metode getResponseHeader():

var XH = null;

vzpostavi();

XH.open("HEAD", "test.htm", true);

XH.onreadystatechange = function() {

if (XH.readyState == 4) {

var d = new Date(XH.getResponseHeader("last-modified"));

alert("Datotka je bila nazadnje posodobljena: " + d);

}

}

XH.send(null);

Vrnjena vsebina je sicer niz, ki pa ga lahko členimo v podatek datumskega tipa z majhno zvijačo: dobljeni niz podtaknemo konstruktorju datumskega tipa, kot vidimo v zgledu. Izid je viden na sliki 2.

Slika 2: Del glave odgovora je tudi datum posodobitve.

Gradnik lahko uporabimo tudi za preverjanje obstoja oz. razpoložljivosti določenega spletnega naslova:

XH.onreadystatechange = function() {

if (XH.readyState == 4) {

if (XH.status == 200) alert("naslov obstaja!")

else if (XH.status == 404) alert("naslov NE obstaja!")

else alert("stanje je " + XH.statusText)

}

}

Kot vidimo, se lahko opremo na številčno kodo v lastnosti status.

Posredovanje parametrov

Za konec si oglejmo še bolj dodelan zgled, ki ponazarja celoten krog komunikacije. Zaradi tega smo pripravili dejavno spletno stran, ki na strani strežnika prepozna posredovane parametre in odda ustrezen odgovor. Za zgled smo nekoliko prilagodili zgled, ki ga je v spletu opisal Jim Ley ([4]).

Spletna stran je pripravljena za strežnik Windows/IIS, ki zna kodo v javascriptu (oziroma jscriptu) izvajati tudi na strani strežnika, prav nič težko pa ne bi bilo zgled predelati v JSP, PHP ali kak drug priljubljen jezik za dinamične spletne strani. Celotna koda je naslednja:

<%@ LANGUAGE = JScript %>

<%

scr = Request.ServerVariables("SCRIPT_NAME")

a =+ (Request.QueryString("a") + "")

b =+ (Request.QueryString("b") + "")

if (isNaN(a) || isNaN(b)) {

a = "";

b = "";

sum = "";

} else {

sum = a + b;

}

acc = Request.ServerVariables("HTTP_ACCEPT") + ""

if (acc.indexOf("sporocilo/x-izracun") != -1) {

Response.Write(sum)

} else {

%><html>

<head>

<title>Izračun</title>

<meta http-equiv="Content-Type"

content="text/html; charset=windows-1250">

<script>

function vzpostavi() {

try {

XH = new ActiveXObject( "Msxml2.XMLHTTP" )

} catch( e1 ) {

try {

XH = new ActiveXObject( "Microsoft.XMLHTTP" )

} catch( e2 ) {

XH = null;

}

}

if ( !XH && typeof XMLHttpRequest != "undefined" ) {

XH = new XMLHttpRequest()

}

}

var XH = null;

vzpostavi();

function izracun() {

url = "<% = scr %>?a=" +

document.frm.a.value + "&b=" + document.frm.b.value;

XH.open("GET",url,true);

XH.onreadystatechange=function() {

if (XH.readyState==4) {

document.frm.sum.value = XH.responseText;

}

}

XH.setRequestHeader("Accept", "sporocilo/x-izracun")

XH.send(null)

// 'submit' se zares ne bo zgodil

return false;

}

</script>

</head>

<body>

<form name="frm" action="<% = scr %>" method="get" onsubmit="return izracun()">

<input type="text" name="a" value="<% = a %>">

+ <input type="text" name="b" value="<% = b %>">

= <input type=text name="sum" value="<% = sum %>">

<input type="submit" value="Izračunaj">

</form>

</body>

</html><%

}

%>

Najprej na strani strežnika ugotovimo lastno ime datoteke, da bomo laže naslovili sami sebe. Zatem preberemo morebiti posredovana parametra a in b in ju, če sta števili, seštejemo. Če je v glavi zahtevka morda oznaka, da odjemalec sprejme tudi tip odgovora "sporocilo/x-izracun", pošljemo samo izid izračuna, sicer oddamo polno spletno stran, kar se bo najverjetneje zgodilo pri prvem obisku.

Sledi torej spletna stran, na kateri je preprost obrazec in skriptna koda, ki se pokliče ob oddaji obrazca. Ker funkcija, ki nadzira oddajo obrazca, vrne neresnično vrednost, se obrazec v resnici ne bo oddal, temveč bo vzpostavil povezavo v ozadju in pridobil izid seštevanja. Omenimo, da je ta mehanizem zelo udoben, saj ob napaki v skriptni kodi, ki jo sodobni brskalniki navadno obravnavajo tiho, obrazec še vedno oddamo na klasični način, kar bo znova povzročilo izračun in pravilen prikaz, le nadležno utripanje in vnovično nalaganje nas bosta spomnila, da nismo opravili povezave v ozadju.

No, če vse teče v redu, v zahtevek kar znotraj naslova URL podtaknemo vrednosti parametrov in ustrezno posodobimo glavo ter pokličemo nazaj na strežnik sami sebe. Dobljenega odgovora seveda ne členimo posebej, saj smo dobili le izid izračuna in čisto nič drugega, zato ga le prepišemo v ustrezno polje.

Nešteto možnosti

Spoznali smo le drobec zmožnosti dinamičnega povezovanja s strežnikom, ki ga omogoča gradnik XML HTTP Request. Skozi zglede smo lahko zaslutili, kakšne nove, razburljive možnosti pri načrtovanju in izdelavi spletnih vmesnikov se nam s tem ponujajo. V kombinaciji s členjenjem DOM in slogi CSS ter vsemi priboljški, ki jih še skriva javascript, res ni čudno, da je spletni brskalnik dom čedalje večjemu številu programov.

Viri in dodatna literatura:

[1] MSDN XML Developer Center (http://msdn.microsoft.com/xml/)

[2] Document Object Model Level 3 Load and Save Specification (http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/)

[3] JavaScript Security in Mozilla (http://www.mozilla.org/projects/security/components/jssec.html)

[4] Using the XML HTTP Request object (http://jibbering.com/2002/4/httprequest.html)

Naroči se na redna tedenska ali mesečna obvestila o novih prispevkih na naši spletni strani!
Prijava

ph

Komentirajo lahko le prijavljeni uporabniki