AJAX
XMLHttpRequest
responseText
responseXML
POST
GET
JavaScript
Børre Stenseth

Ajax

Hva

Mye av arbeidet med å lage nettbaserte løsninger går ut på å fordele arbeidet mellom tjener og klient. I dette materialet konsentrer vi oss i all hovedsak om forholdet mellom en nettleser og et CGI-script (skrevet i Python).

bottle
... greide ikke la være

Vi kan laste ned mer data enn det som vises fram ved å legge dem enten som "XML-islands" eller vi kan laset opp masse materiale og legge dem slik at de er usynlige. Brukerens interaksjon med nettleseren kan ordne og vise slike skjulte data. Dette ordner vi med javaskripting.

I disse modulene skal vi gå et skritt videre i å flytte logikk til klientene, nettleseren. Vi skal lage HTML-sider som oppdateres dynamisk, uten at hele siden oppfriskes.

Det er to objekter som JavaScript kjenner i en html-side som er spesielt aktuelle i sammenhengen:

  • document. DOM-objektet er brukt i mange sammenhenger i dette materialet. DOM er ikke en nødvendig del av den mekansimen vi skal se på her, men det er svært nyttig når vi skal foreta ikke-triviell behandling av mottatte data.
  • XMLHttpRequest. Det er dette objektet som setter oss i stand til å hente data fra en annen URL. De dataene vi henter kan vi plassere ut på vår HTML-side eller vi kan bruke dem til å kontrollere brukerens inn data eller hva vi måtte finne på.

Du kan betrakte materialet på denne siden som en introduksjon til basis funksjonene. Min anbefaling er å bruke et vel testet bibliotek til AJAX. Jeg bruker jQuery, som beskrevet i modulen jQuery

Basisfunksjoner

Koden under er svært enkel og ikke spesielt funksjonell. Vi definerer en global variabel, myRequest, med de farer dette medfører dersom vi har flere asynkrone forespørsler på samme side. Den fungerer for enkle demo-situasjoner, men det er grunn til å vurdere å basere AJAX-løsninger på et litt mer robust grunnlag, se f.eks. modulen jQuery .

var myRequest;
function startXMLHttpRequest(url)
{
    myRequest=null;
    myRequest = new XMLHttpRequest();
    if (myRequest) {
        myRequest.onreadystatechange = processRequest;
        myRequest.open("GET", url, true);
        myRequest.send(null);
    }
    else{
        alert("Nettleseren henger ikke med");
    }
}
function processRequest()
{
    // if the request is complete and successfull
    if (myRequest.readyState == 4) {
        if ((myRequest.status == 200) || (myRequest.status == 304))
            alert(myRequest.responseText);
        else
            alert("Problem med tilgang til data:\n" + myRequest.statusText);
    }
}

Koden over definerer et "globalt" XMLHttpRequest-objekt og to metoder. Den første metoden, startXMLHttpRequest, adresserer en fil via en URL. Merk at denne metoden ikke tar hensyn til gamle, eldre enn versjon 7, versjoner av IE. Dersom vi skulle gjøre det, ville vi kanskje ende opp med noe slikt:

_alternativ kode

Når vi har fått opprettet objektet ser vi at vi angir hvilken funksjon som skal være eventhandler: "myRequest.onreadystatechange = processRequestChange;". Deretter åpner vi en GET-forespørsel og vi sender den uten tilleggsdata. De aktuelle metodene og egenskapene ved et XMLHttpRequest-objekt er listet nedenfor.

Den andre funksjonen, processRequestChange, er programmert slik at den bare bekymrer seg for tilstanden som tilsier at forespørselene er ferdig behandlet(4). Dersom status på dette tidspunktet er 200 så har alt gått bra og vi kan plukke opp resultatet som text og viser det fram i en alert-boks.

Du kan teste ved å klikke

Fragmentet som sender forespørselen:

<p>
    Du kan teste ved å klikke 
    <button class="demobutton" onclick="javascript:startXMLHttpRequest('frej1.txt');return false;">her.</button>
</p>
    

En viktig begrensning

JavaScript er underlagt begrensninger i hva vi kan få tak i via koden. Begrensningene administreres av nettleserne. Begrunnelsen er sikkerhet. Når vi lager AJAX-løsninger må de ressursene, filene, vi bruker ligge i samme domene som den HTML-siden som bruker dem. Denne sikkerhetsmekanismen er ganske involvert og det vil føre for langt å forfølge dette her. Kortformen for våre praktiske løsninger er at vi legger alt vi vil bruke på samme server og samme bruker.

I praksis er ikke dette noen alvorlig begrensning. Det normale er vel at vi bygger en løsning med tilgjengelige ressurser innenfor et domene. Det er ikke noe problem å bruke fremmede ressurser, men da gjør vi det via kode på tjeneren, f.eks. et pythonskript, som legges i det lovlige domenet. Webservices kan være eksempler på slike "fremmed" ressurser. Vi kan selvsagt også hente data fra databaser på andre tjenere, typisk en 3-lags løsning.

Noen løsninger

Tekst til DOM-node

Vi har altså, hvis øvelsen ovenfor gikk bra, greidd å få tak i en tekst fra en URL. Når vi nå har tak i den burde vi kunne legge den ut på siden i stedet for å vise den fram i en alert boks. Vi bruker altså DOM-programmering for å plassere den teksten vi henter.

Test her https://borres.hiof.no/wep/js/ajax/demo2/page2.html

Velge blandt flere

En vanlig situasjon er selvsagt at vi ønsker å tilfredsstille et valg som brukeren treffer. brukeren vil velge blandte flere alternativer. Eksempelet viser hvordan vi kan kople inn en select-boks.

Test her https://borres.hiof.no/wep/js/ajax/demo4/page4.html

Header informasjon

Vi kan sende en forespørsel som bare returnerer header-informasjon fra en fil. På denne måten kan vi finne ut når en ressurs sist ble oppdatert, mime-typen, servertypen etc. Vi bruker HEAD i stedet for GET eller POST. Vi henter ut getAllResponseHeaders() i stedet for responseText.

Test her https://borres.hiof.no/wep/js/ajax/demo3/page3.html

Tolke resultatet som XML

Vi kan tolke det svaret vi får på en forespørsel som en DOM-struktur. Det vil si at vi kan hente en XML-fil, analysere denne som et DOM-tre og bruke de delene vi vil til å møblere HTML-siden, som vi betrakter som et annet DOM-tre. Vi plukker da opp responseXML i stedet for responseText.

Test her https://borres.hiof.no/wep/js/ajax/demo5/page5.html

Bruk av pythonskript på tjeneren

I stedet for å laste ned en fil direkte, kan vi la kode på tjeneren returnere de dataene vi er ute etter, i den form vi vil ha dem. Vi må da tenke gjennom arbeidsdelingen mellom klient og tjener. Eksempelet demonstrerer hvordan vi kan hente 1 av Shakespears sonetter via et Pythonskript på tjeneren.

Test her https://borres.hiof.no/wep/js/ajax/demo6/page6.html

Skriptet som betjener siden:

_shakereturn

Bruk av pythonskript på tjeneren, POST

Samme som ovenfor, men vi bruker POST istedet for GET. Pythonkoden er den samme som over.

Test her https://borres.hiof.no/wep/js/ajax/demo7/page7.html

Et enkelt "vevsted"

Et enkelt vevsted med 3 sider, eller egentlig en side der hovedinnholdet byttes ut.

Test her https://borres.hiof.no/wep/js/ajax/demosite/index.html

GET eller POST

Vi kan hente data med GET eller med POST. POST har den egenskapen at den kan transportere større datamengder. Anta at vi har etablert et XMLHttpRequest-objekt og at vi har gjort klar parameterlista, params.

POST:

...
myRequest.open("POST", url,true);
myRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
myRequest.setRequestHeader("Content-length", params.length);
myRequest.setRequestHeader("Connection", "close");
myRequest.send(params);

GET:

...
myRequest.open("GET", url+'?'+params, true);
myRequest.send(null);

Metoder og egenskaper

Et XMLHttpRequest-objekt har følgende metoder og egenskaper:

Metoder

abort()
Kansellerer pågående forespørsel
getAllResponseHeaders()
Returnerer et komplett sett av alle "http headers" som text
getResponseHeader("headername")
Returnerer verdien av en "http header"
open("method","URL",async,"user","password")
Spesifiserer metoden URL-adressen og tre valgfrie andre parametre i en forespørsel
method kan ha verdiene "GET", "POST", eller "PUT" ("GET" når vi vil hente data og "POST" når vi sender data. Når vi sender mer enn 512 byte må "POST" brukes).
URL parameteren kan være absolutt eller relativ.
async parameteren spesifiserer om forespørselen skal behandles asynkront eller ikke. Dersom den er true betyr det at skriptet kan fortsette uten å vente på svar.
send(content)
Sender forespørsel. content legger data til en forespørsel som er åpnet med POST
setRequestHeader("label","value")
Legger til et par(navn og verdi) til en "http header" som skal sendest

Egenskaper

onreadystatechange
Inneholder en funksjon, en event handler, som skal benyttes når tilstanden til XMLHttpRequest-objektet endres.
readyState
Returnerer tilstenden til objektet:
  • 0 = ikke initialisert
  • 1 = lastes
  • 2 = lastet
  • 3 = interktivt
  • 4 = komplett
responseText
Returnerer responsen på en forespørsel som en tekst
responseXML
Returnerer responsen på en forespørsel som XML. Responsen er i form av et XML dokument som kan parses ved hjelp av DOM-metoder.
status
Returnerer status som et tall (f.eks.: 404 for "Not Found" eller 200 for "OK")
statusText
Returnerer status som en tekst(f.eks.: "Not Found" eller "OK")