Teil 1: AJAX - Asynchronous JavaScript and XML
XMLHttpRequest-Klasse
Verschiedene Wege zum dynamischen Laden von Informationen
Übertragung von Text vs. XML
JSON – Übertragungsmodell Teil 2: Websockets
Protokoll: HTTP und Upgrade
Clientseitige Schnittstellen
Beispiel für serverseitige Programmierung von Websockets (für PHP)
5. AJAX und Websockets
Bislang:
Webinhalte laden durch
Eingabe einer URL
Klicken einer Link-URL
Absenden eines Formulars (GET, POST)
HTTP- Protokoll
Immer Aufbau einer neuen
HTTP-Interaktionen (1)
Webbrowser
Webserver
Gewünschte Erweiterung:
Interaktives Nachladen von Webinhalten, ohne jedes mal eine neue Webseite zu laden.
Beispiele:
Mausklick auf Bereich → Nachladen eines Details
Nachladen von Hilfe- und Erklärungstexten
Formulare mit Input- Feldern, die nur in
Spezialfällen erscheinen
HTTP-Interaktionen (2)
Interaktives Anzeigen und dynamischer Webseitenaufbau sind bereits durch JavaScript möglich.
Grenzen von JavaScript (ohne die nachfolgende Erweiterung):
Zugriff auf Dateien der Client-Seite nicht möglich
Zugriff auf Dateien auf Serverseite bislang auch nicht möglich Grenzen von PHP:
Zugriff auf Dateien und Datenanken ist immer mit dem vollständigen Laden einer PHP-Seite verbunden
Benötigt wird:
Nachlade-Funktion innerhalb Client-Script (JavaScript), die wie der
Browser selbst HTTP-Protokollaktionen auslösen und Ergebnisse
entgegen nehmen kann.
Neue JavaScript-Klasse XMLHttpRequest
XMLHttpRequest-Klasse
Webseite mit Javascript
Browser XMLHttpRequest-
Objekt
ggf. mit Scripting
Web-Server
Asynchroneous JavaScript and XML (AJAX)
ermöglicht Webseiten, die sich wie lokale Anwendungs-GUIs verhalten
Teilinhalte können verändert bzw. aktualisiert werden, ohne die ganze Webseite neu zu laden
Schnelleres Reagieren auf Nutzerinteraktionen, da weniger Daten übertragen werden
Übertragen von eingegebenen Nutzerdaten an den Server (ohne Neuladen der Seite, wie beim alten klassischen
Formular-Submit)
Asynchrone Kommunikation mit dem Webserver, d.h. von der Oberfläche im zeitlichen Verhalten entkoppelt
AJAX Technologie
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head> <title>JavaScript und HTTP</title>
<script type="text/javascript">
//<![CDATA[
window.onload = function() {
var req = (window.XMLHttpRequest) ? new XMLHttpRequest() : ((window.ActiveXObject) ? new
ActiveXObject("Microsoft.XMLHTTP") : false );
req.open("GET", "dyntext.txt", true);
req.onreadystatechange = function() { if (req.readyState==4)
{ if (req.status == 200) // Fortsetzung auf der folgenden Seite
XMLHttpRequest – Ein Minimalbeispiel (1)
Fortsetzung:
{ var d = document.getElementById("dyntext");
d.innerHTML = req.responseText;
} } }
req.send(null);
} //]]>
</script>
</head>
<body>
<div id=“dyntext"></div> // hier erscheint der nachgeladene Text
</body>
</html>
XMLHttpRequest – Ein Minimalbeispiel (2)
Das Skript setzt voraus, dass auf dem Server eine Datei “dyntext.txt“
vorhanden ist.
Deren Inhalt wird in die Webeseite eingebaut.
Verschiedene Klassen in Browsern, je nach Plattform (Microsoft, Gecko-Browser, u.a.)
Instanziierung der Klasse für Gecko1-basierte Browser (Mozilla, Firefox, Thunderbird):
var req = new XMLHttpRequest();
Instanziierung der Klasse für Microsoft-basierte Browser (IE):
var req = new ActiveXObject(“Microsoft.XMLHTTP“);
Zum Teil werden ausgewählte Versionen des XMLHTTP-Objekts benötigt:
var req = new ActiveXObject(“Msxml2.XMLHTTP.5.0“);
1 Gecko ist eine s.g. Rendering Engine, die von der Mozilla Foundation entwickelt wurde
XMLHttpRequest - Klasse
Benutzung des ?-Operators in Zuweisungen:
var abs_x = (x>0) ? x : x*(-1);
Versuch, die geeignete Klasse zu instanziieren:
var req = (window.XMLHttpRequest) ? new XMLHttpRequest() : ((window.ActiveXObject) ?
new ActiveXObject(“Microsoft.XMLHTTP“): false );
Damit „laufen“ Ajax-Webseiten sowohl in Microsoft, als auch in Gecko- Browsern
XMLHttpRequest - Instanziierung (1)
Bedingung rechte Seite wenn erfüllt
rechte Seite wenn nicht erfüllt
try-catch Kaskade zum erreichen der bestmöglichen AJAX- Unterstützung:
<script type="text/javascript">
//<![CDATA[
if (window.ActiveXObject) { try {
req = new ActiveXObject(“Msxml2.XMLHTTP.5.0“);
} catch (e) { try {
req = new ActiveXObject(“Msxml2.XMLHTTP.4.0“);
} catch (e) { try {
req = new ActiveXObject(“Msxml2.XMLHTTP.3.0“);
} catch (e) { try {
req = new ActiveXObject(“Microsoft.XMLHTTP“);
} catch (e) { req = false; } }}}
XMLHttpRequest - Instanziierung (1)
try-catch Kaskade über Array:
<script type="text/javascript">
//<![CDATA[
var msxml = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP",
"Microsoft.XMLHTTP„ ];
if (window.ActiveXObject) {
for (var i=0; i<msxml.length; i++) { try {
ret = new ActiveXObject(msxml[i]);
break;
} catch(e) {}
} } //]]>
</script>
XMLHttpRequest Instanziierung (2)
Laden einer Textdatei über XMLHttpRequest:
Funktion open(“GET“, “dateiname.txt“, …)
Entnahme des Inhalts aus dem Element responseText Laden einer XML-Datei:
Funktion open(“GET”, “dateiname.xml”, …)
Entnahme der Daten aus responseXML
Traversal des DOM-Trees des XML-Objekts und Einbauen der
Informationen in die Webseite (siehe loadxmlobj.js aus den Übungen) Laden von HTML-formatierten Inhalten aus einer Datei:
Inhalte als Text anfordern, z.B. open(“GET“,“info.txt“, …)
Einfügen der formatierten Inhalte aus responseText als innerHTML bereits existierender Elemente in der Webseite
Wege zum dynamischen Laden (1)
Laden einer Skriptdatei über XMLHttpRequest:
Funktion open(“GET“, “dateiname.php“, …)
Bei Anforderung des Objekts kann serverseitig z.B. die Abfrage von Informationen aus einer Datenbank, oder das Lesen von Daten aus einer Datei und eine anschließende Formatierung erfolgen.
Mit AJAX wird oft eine Kombination von server- und client-seitigem Scripting realisiert.
Wege zum dynamischen Laden (2)
Ausgangspunkt ist das Request-Objekt req, das wie vorab beschrieben instanziiert wurde.
Es existiert eine Schnittmenge von Methoden die gleich arbeiten
req.setRequestHeader(key,value) – erlaubt das Setzen eines optionalen Request Headers
req.open(method, url, [syncFlag, username, password]) – der Request wird an die gewünschte Zieladresse (url) gerichtet.
Die Methode kann GET, POST, HEAD, PUT, DELETE sein.
Das syncFlag gibt an, ob synchron (false) oder asynchron (true, Standardwert) gearbeitet werden soll.
Eine Authentifikation mittels username, password ist wäre bei den Methoden PUT oder DELETE notwendig.
Methoden XMLHttpRequest / ActiveXObject (1)
Request-Objekt Methoden (Fortsetzung):
req.send(body|null) – erst mit send erfolgt das Absenden des
Requests, danach kann unmittelbar die Antwort des Servers folgen, sofern eine asynchrone Übertragung vereinbart wurde.
Im body können Nutzerdaten an den Server übertragen werden.
req.abort() – beendet den aktuell ausgelösten Request
req.getResponseHeaders() – erzeugt eine Liste aller vorhandenen Header als key-value-Paare in einem String
req.getResponseHeader(name) – gibt den Wert des durch name angefragten Headers zurück
Methoden XMLHttpRequest / ActiveXObject (2)
Request-Objekt Methoden (Fortsetzung):
Ein gültiger Request muss mindestens mit den Methoden open() und send() vorbereitet werden.
Beispiel:
var req = new XMLHttpRequest;
req.open(“GET“,“beispiel.xml“);
req.send(null);
…
Bei asynchroner Übertragung wird das Ergebnis von einer Funktion ausgelesen, die als req.onreadystatechange angemeldet wurde.
Siehe Eigenschaften.
Methoden XMLHttpRequest / ActiveXObject (3)
Request-Objekt-Eigenschaften:
Auch hier gibt es eine funktional gleich gestaltete Schnittmenge
req.readyState – gibt den Status des Requests wieder:
0 – unititialized (noch kein open() ausgeführt)
1 – loading (wird gestartet, aber noch nicht abgeschickt durch send()) 2 – loaded (bereit durch send() abgeschickt,
aber Antwort steht noch aus)
3- interactive (die Übertragung durch den Server läuft und Teile der Antwort sind bereits in responseText bzw.
responseXML verfügbar) 4 – complete (alles vollständig übertragen)
req.onreadystatechange – Platz für eine zuzuordnende Funktion, die bei jeder Änderung von req.readyState ausgeführt wird. Diese Funktion erlaubt, den Zeitpunkt zu finden, an dem das Ergebnis des Request vorliegt.
Eigenschaften XMLHttpRequest / ActiveXObject (1)
Request-Objekt-Eigenschaften (Fortsetzung):
req.responseText – Inhalt des Bodys (Typ String) aus der Server- Antwort
req.responseXML – XML-Objekt aus der Server-Antwort, sofern eine XML-Datei angefordert wurde
req.status – numerischer Wert des Serverstatus am Ende der Übertragung, wenn readystate den Wert 4 enthält, z.B. 200 für erfolgreiche Übertragung, oder 400 für ‘bad request‘
req.statusText – eine Beschreibung des Status am Ende der Übertragung (bei readyState==4) als String
Eigenschaften XMLHttpRequest / ActiveXObject (2)
var req.onreadystatechange = function() {
if (req.readyState==4) {
if (req.status==200)
{ /* Antwort weiter verarbeiten */}
else {
alert(“Request Response Code:“+req.Status);
} } }
Beispiel
Überwachung eines asynchronen Requests
Laden einer Skriptdatei über XMLHttpRequest mit Übergabe von Parametern
Funktion
open(“GET“, “dateiname.php?param1=wert1¶m2=wert2“, …) Parameterauswertung im PHP-Skript:
…
<?php
if (isset($_GET[“param1“]))
// weitere Verwendung von $_GET[“param1“], z.B. als DB Abfrage
$sql = “SELECT name FROM tabelle WHERE wohnort=\“ “.
$_GET[“param1“].“\“ “;
$ergebnis = mysql_query($sql, $dbverbindung);
…
echo “$zeile[i]“;
?>
Beispiel für einen GET-Request
Ein GET-Request sendet die
übergebenen Daten mit der URL mit.
Methodenaufrufe zur Zusammenstellung des Requests req.open(“POST“, “dateiname.php“, …);
req.setRequestHeader(“Content-Type“,
“applicatio/x-www-form-urlencoded“);
req.send(“x=1&y=3&z=5“);
Parameterauswertung im PHP-Skript:
…
<?php
$x=0; $y=0; $z=1;
if (isset($_POST[“x“]) $x=$_POST[“x“];
if (isset($_POST[“y“]) $y=$_POST[“y“];
if (isset($_POST[“z“]) $z=$_POST[“z“];
$erg=($x + $y) * $z ;
echo “ ($x + $y) * $z ergibt $erg \n“;
…
Beispiel für einen POST-Request
Ein POST-Request sendet die
übergebenen Daten mit dem Body-Block.
Ein AJAX-“Taschen“-Rechner
<html>
<head> <title>JavaScript und HTTP</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src=“ajaxrechner.js" type="text/javascript"></script>
</head>
<body>
<form>
<input type="text" name="a" />
<select name="o">
<option value="p">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
AJAX Beispiel (1)
Ein AJAX-“Taschen“-Rechner (Fortsetzung)
…
<input type="text" name="b" />
<input type="button" value=" = "
onclick=" ajaxRechner(this.form)" />
<input type="text" name="ergebnis" />
</form>
</body>
</html>
JavaScript (in eigenständiger Datei “ajaxrechner.js“) …
AJAX Beispiel (2)
function ajaxRechner(f)
{ var query = "a=" + escape(f.a.value) + "&" + "b=" + escape(f.b.value) + "&" +
"o=" + escape(f.o.options[f.o.selectedIndex].value);
alert("Aufruf ajaxrechner: POST, body="+query);
f.ergebnis.value = "Rechnen ..."
var req = new XMLHttpRequest;
req.open("POST", "rechner.php",true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.onreadystatechange = function() { if (req.readyState==4)
{ if (req.status==200)
f.ergebnis.value = req.responseText;
else
alert("Request Response Code:"+req.Status);
} }
AJAX Beispiel (3)
Rechner.php:
<?php
if ( $_POST["a"]!="" && $_POST["b"]!="" ) { $a = $_POST["a"];
$b = $_POST["b"];
switch ($_POST["o"])
{ case "p": print $a+$b; break;
case "-": print $a-$b; break;
case "*": print $a*$b; break;
case "/": print $a/$b; break;
} }
else print “Eingabefehler";
?>
AJAX Beispiel (4)
var req = new XMLHttpRequest;
Asynchrone Natur von XMLHttpRequest
req.open(“POST“,“ziel.php“);
req.send(“name=Meier&kontonr=“4711815“);
Zeit
result = req.responseText;
Ajax-Engine Web-Client
Web-Server
HTTP-Protokoll- Nachrichten
var req = new XMLHttpRequest;
Zustände von XMLHttpRequest
0
unitialized
1 loading
2 loaded
3
interactive
4 complete req.open(“POST“,“ziel.php“);
req.send(“name=Meier&kontonr=“4711815“);
Server-Antwort begonnen
Server-Antwort beendet
Client-Script:
Zusammenstellen des Requests
Client-Script:
Verarbeiten der
Antwort, bzw. Fehlerbehandlung Client-Script:
ggf. Abbrechen, optional kann eine Fortschrittsanzeige durch
zeitgesteuerte Funktionen erfolgen
Bisher zwei Möglichkeiten zur Datenübertragung mit dem XMLHttpRequest
Anfordern von Daten und Rückgabe
als responseText
als reponseXML Übertragen von Daten
als url-encoded Text mit einen GET-Request
als url-encoded String (Text) im send-body (POST Request)
als XML-Datenstruktur im send-Body (POST Request)
Übertragung von Text vs. XML
Übertragen der XML-Datenstruktur im send-Body (POST Request) var req = XMLHttpRequest;
req.open(“POST“,“beispiel.php“);
req.setRequestHeader(“Content-Type“,“text/xml“);
req.onreadystatechange = function() { … }
var namestr = encodeURI(namefeld.value); // angenommene
var alterstr = encodeURI(alterfeld.value); // Formular Text-Inputs var xmlbody = “<search>“;
xmlbody += “<name>“+namstr+“</name>“;
xmlbody += “<alter>“+alterstr+“</alter>“;
xmlbody += “</search>“;
req.send(“<?xml version=‘1.0‘ encoding=‘UTF-8‘?>“+xmlbody);
Eingabeparameter als XML
Anlegen einer Variablen zur Aufnahme des XML-Objekts var xmlobj = req.responseXML;
Danach Auslesen der Elemente aus dem XML-Dokument. Das konkrete Vorgehen ist stark abhängig von der XML-Struktur.
Ein einfaches Beispiel:
var element = xmlobj.getElementsByTagName(‘abflug‘).item(0);
var value = element.firstChild.nodeValue;
Korrespondiert z.B. mit der empfangenen Struktur:
Rückgabe als XML
<flugplan ort=“FRA“>
<abflug>Moscow</abflug>
<abflug>Paris</abflug>
<ankunft>Stockholm</ankunft>
</flugplan>
Möglichkeit 1: Manuelles Zusammenstellen des XML-Strings mit echo und printf Anweisungen.
<?php
$stationsmeldung = array(“ort“=>“Stavanger“, “windr“=>“Sued“,
“windst“=>“4“, “wetterer“=>“Regenschauer“,
“temp“=>“9“, “druck“=>“1009“ );
…
echo “<stationsmeldung> \n“;
while($wert=each($stationsmeldung)) { echo “<“.$wert[“key“].“>“;
echo $wert[“value“];
echo “</“.$wert[“key“].“>“;
}
echo “</stationsmeldung> \n“;
?>
XML auf Server-Seite (1)
Möglichkeit 2: Benutzen von Bibliotheksklassen, z.B. DOM-XML
Methoden sind nach dem Vorbild der DOM-Funktionen von JavaScript gestaltet
<?php
$stationsmeldung = array(“ort“=>“Stavanger“, “windr“=>“Sued“,
“windst“=>“4“, “wetterer“=>“Regenschauer“,
“temp“=>“9“, “druck“=>“1009“ );
…
$dom = domxml_new_doc(‘1.0‘, ‘utf-8‘);
// Root Knoten erstellen
$root = $dom->create_element('stationsmeldung');
$dom->append_child($root);
…
XML auf Server-Seite (2)
Möglichkeit 2 (Fortsetzung) : Bibliotheksklassen DOM-XML
…
while($wert=each($stationsmeldung)) {
$tag_node = $dom->create_element($wert[“key“]);
$root->append_child($tag_node);
$tag_node->append_child( $dom->create_text_node($wert[“value“]));
}
// Ausgabe
echo $dom->saveXML();
?>
Neben DOM-XML gibt es: DOM, libxml, SimpleXML und vermutlich noch weitere PHP-Bibliotheken
XML auf Server-Seite (3)
Daten, die mehrmals per AJAX-Request angefordert werden, haben oft die gleiche URL und unterliegen damit Caching.
Cache des Browsers
Cache in Proxy-Servern
Es wird veralteter Inhalt zurückgegeben, wenn die Daten serverseitig aktualisiert werden.
Gegenmittel auf Client-Seite:
Setzen zusätzlicher Request-Header:
req.setRequestHeader(“Pragma“,“no-cache“);
req.setRequestHeader(“Cache-Control“,“must-revalidate“);
oder Anhängen zusätzlicher Dummy-Parameter:
var zusatz = new Date().getTime();
req.open(“GET“,“beispiel.php“+zusatz);
Umschiffen von Cache-Problemen (1)
Gegenmittel auf Server-Seite:
Setzen zusätzlicher Response-Header:
<?php
header(“Pragma“,“no-cache“);
header(“Cache-Control: no-store, no-cache, must-revalidate“);
header(“Cache-Control: post-check=0, pre-check=0, false“);
oder Setzen eines Cache-Verfallsdatums auf ein früheres Datum:
header(“Expires: Tue, 27. Jul 1997 06:00:00 GMT “);
header(“Last-Modified: “.gmdate(“D, d M Y H:i:s“).“ GMT“);
Umschiffen von Cache-Problemen (2)
JSON - JavaScript Object Notation
Übertragung von speziell kodierten Strings anstatt XML
Strings werten als Text übertragen (z.B. req.responseText)
Für das Senden via JSON verpackt der Client die
Datenstrukturen (bzw. Objekte) in diese Strings. Das geschieht mittels bereitgestellter Bibliotheksfunktionen. Bei JavaScript entstehen s.g. Literalobjekte
Client entnimmt Daten und baut Datenstrukturen (bzw.
Objekte) wieder auf. Die Auswertung auf Client-Seite erfolgt mittels Bibliotheksfunktionen und erzeugt wiederum solche Literalobjekte.
Server (z.B. PHP-Skript) kann Datenstrukturen in JSON- Strings kodieren und als Ausgabe eines HTTP-Requests bereitstellen. Dazu gibt es in PHP entsprechende
Bibliotheksfunktionen.
JSON – Übertragungsmodell (1)
Beispiel: ein AJAX-Client sendet eine Übersetzungsanfrage per GET mit dem zu übersetzenden Wort als Parameter:
Client / Javascript:
$word = “funicular“
req.open(“GET“,“server.php?translate=“+$word, true);
req.send();
Server / PHP:
<?php
require_once(“json.php“);
$word = $_GET[‘translate‘];
// some magic translation algorithm
$german = “seilbahn“; $spanish=“el funicular“; $french=“funiculaire“;
$result = array (“g“=>$german, “s“=>$spanish, “f“=>$french);
$json = new Services_JSON();
echo $json->encode($result);
JSON – Übertragungsmodell (2)
Beispiel (Fortsetzung) Client / JavaScript:
in der onreadystatechange-Funktion if (req.readystate==4)
{ if (req.status==200)
{ var translation = eval ( ‘(‘+req.responseText+‘)‘ );
d_german.innerHTML= translation.g;
d_french.innerHTML= translation.f;
d_spanish.innerHTML= translation.s;
}
else {
d_german.innerHTML= “Translation Error“;
d_french.innerHTML= “Translation Error“;
d_spanish.innerHTML= “Translation Error“;
}
JSON – Übertragungsmodell (3)
Übertragungsformat des Beispiels:
{“g“: “seilbahn“,“s“:“el funicular“,“f“:“funiculaire“}
Im Beispiel wurde ein assoziatives Array kodiert.
Ein anderes Beispiel ist ein 2-dimensionales Array innerhalb des Clients:
innere Dimension als assoziatives Array, äußere Dimension als numerisches Array.
var warenkorb = {
“items“: [ {id:1, titel:“Schokolade“, preis:“1.05“, waehrung:“EUR“ }, {id:2, titel:“Gummibaeren“, preis:“1.99“, waehrung:“CHF“}, {id:3, titel:“Mandeltorte “, preis:“29.95“, waehrung:“SKR“ } ] };
…
JSON – Übertragungsmodell (4)
…
var json_string = JSON.stringify(warenkorb);
Danach sieht der String wie folgt aus:
{"items":[{"id":1,"titel":"schokolade","preis":1.05,"waehrung":"EUR"},{"id":2,"tit el":"Gummibaeren","preis":1.99,"current":“CHF"},{"id":3,"titel":“Mandeltorte","
preis":19.95,"current":“SKR"}]}
Dieser String könnte nun zum Server übertragen und dort gespeichert werden. Wird der String später wieder zum Client gesendet, kann er wie folgt wieder in die originale Datenstruktur gewandelt werden:
var warenkorb= JSON.parse(receivedText);
Alternativ kann man auch verwenden:
var warenkorb= eval(‘(‘+receivedText+‘)‘);
JSON – Übertragungsmodell (5)
HTTP-Interaktion durch Klasse XMLHttpRequest im JavaScript- Teil einer Webanwendung
Laden und Übertragen von Webinhalten werden damit losgelöst von der Bedienung des Web-Browsers
Client-Seite: JavaScript
Server-Seite: normaler HTTP-Server + XML Dateien + PHP oder andere Skriptsprachen