Ajax meistern, Teil 2: Wie Sie mit JavaSkript und Ajax asynchron anfragen

 


Brett McLaughlin (brett@newInstance.com), Autor und Editor, O'Reilly Media Inc.

17. Januar 2006

Deutsche Übersetzung: Claudia Nölker

Die meisten Web-Anwendungen nutzen ein Request-Response-Modell, bei dem eine komplette HTML-Seite vom Server geliefert wird. Das Ergebnis ist ein Hin und Her mit dem üblichen Ablauf: Button anklicken, auf den Server warten, einen anderen Button anklicken und wieder auf den Server warten. Mit Ajax und dem XMLHttpRequest-Objekt können Sie ein Request-Response-Modell nutzen, bei dem die Benutzer nicht auf die Antwort des Servers warten müssen. In diesem Artikel zeigt Brett McLaughlin, wie Sie XMLHttpRequest-Instanzen browserübergreifend erstellen, damit Anfragen absenden und die Antwort des Servers verarbeiten.

Im vorherigen Artikel dieser Serie (siehe Link in den Ressourcen) haben Sie Ajax-Anwendungen und deren grundlegende Konzepte kennen gelernt. Deren Dreh- und Angelpunkt sind eine Anzahl von Technologien, die Sie wahrscheinlich bereits kennen: JavaScript, HTML und XHTML, etwas dynamisches HTML und sogar etwas DOM (Document Object Model). Nach diesem groben Überblick wenden wir uns in diesem Artikel den kleinen, aber feinen Ajax-spezifischen Details zu.

Wir beginnen mit dem Grundbaustein aller auf Ajax basierenden Programmieransätze: dem XMLHttpRequest-Objekt. Dieses Objekt zieht sich wie ein roter Faden durch alle Ajax-Anwendungen und -- Sie ahnen es wahrscheinlich schon -- es ist essentiell dieses Objekt durch und durch zu kennen, damit Sie dessen Möglichkeiten optimal ausschöpfen können. Tatsächlich ist es so, dass Sie manchmal eben genau das XMLHttpRequest-Objekt nicht verwenden, damit Sie es richtig einsetzen. Was in aller Welt soll denn das jetzt?

Ein kurzer Blick auf Web 2.0

Zuerst wieder ein kleiner Überblick, bevor wir uns auf den Code stürzen -- stellen Sie sicher, dass Ihnen das Konzept vom Web 2.0 glasklar ist. Wenn Sie den Ausdruck "Web 2.0" hören, sollten Sie als Erstes fragen: "Was ist Web 1.0?" Obwohl Sie nur selten den Begriff Web 1.0 hören werden, ist damit das traditionelle Web mit dem expliziten Request-Response-Modell gemeint. Gehen Sie zum Beispiel zu amazon.de und klicken Sie auf einen Button oder starten Sie eine Suche. Dann wird eine Anfrage an den Server gestellt und eine Antwort kommt von dort wieder zurück zu Ihrem Browser. Diese Antwort enthält jedoch eine Menge mehr als bloß eine Liste von Büchern und Titeln: Es ist tatsächlich eine komplette HTML-Seite. Als Ergebnis sehen Sie wahrscheinlich ein kurzes Flackern während Ihr Web-Browser den Bildschirm neu aus der HTML-Seite aufbaut. Mit jeder neuen Seite, die Sie sehen, haben Sie ein Beispiel für das Request-Response-Modell vor sich.

Das Web 2.0 hat sich (zu einem großen Teil) von diesem Hin und Her verabschiedet. Schauen Sie sich zum Beispiel Google Maps oder Flickr an. (Die Links zu diesen auf Ajax basierenden Web 2.0-Seiten finden Sie in den Ressourcen.) Bei den Google Maps können Sie sich die Landkarte mit der Maus an die richtige Position ziehen und dann hineinzoomen, ohne dass viel auf der Seite neu gezeichnet wird. Natürlich finden Anfragen und Antworten statt, doch das läuft hinter den Kulissen ab. Als Benutzer ist die Interaktion mit der Seite viel angenehmer und erinnert an eine Desktop-Anwendung. Dieses neue Gefühl und Paradigma ist gemeint, wenn von Web 2.0 die Rede ist.

Uns interessiert dabei, wie diese neue Interaktionsform möglich wird. Natürlich müssen Sie weiterhin Anfragen machen und Antworten verarbeiten, doch das ständige Erzeugen von HTML-Code nach jeder Anfrage sorgt für den Eindruck einer langsamen, trägen Web-Schnittstelle. Also brauchen Sie einen Ansatz, mit dem Sie Ihre Anfragen und die Antworten darauf nur auf die Daten, die Sie wirklich brauchen (und nicht die ganze HTML-Seite), begrenzen können. Eine komplett neue HTML-Seite benötigen Sie nur noch dann... wenn... naja, wenn der Benutzer eine neue Seiten sehen soll.

Doch die meisten Interaktionen ändern einfach nur Details oder tauschen Inhalte von existierenden Seiten aus. In all diesen Fällen ist es mit Ajax und dem Web 2.0-Ansatz möglich, Daten zu senden und zu empfangen, ohne dafür gleich die ganze HTML-Seite zu aktualisieren. Und jedem Vielsurfer wird durch diese Fähigkeit Ihre Anwendung schneller und dynamischer vorkommen -- und dafür sorgen, dass er wiederkommt.

Grundlegendes zum XMLHttpRequest

Damit dieses kleine Wunder vollbracht wird, müssen Sie sich mit einem JavaScript-Objekt namens XMLHttpRequest eingehend vertraut machen. Dieses kleine Objekt -- dass es in den Browsern eigentlich schon eine ganze Weile gibt -- ist der Schlüssel zu Web 2.0, Ajax und zu ziemlich allem anderen, was Sie in dieser Serie lernen können. Als kurzer Überblick sind hier einige der Methoden und Eigenschaften aufgeführt, die Sie von diesem Objekt nutzen werden:

  • open(): initialisiert die neue Anfrage an den Server
  • send(): sendet die Anfrage zum Server
  • abort(): bricht die aktuelle Anfrage ab
  • readyState: enthält den aktuellen Verbindungsstatus
  • responseText: der Text, den der Server als Antwort auf die Anfrage zurück schickt

Machen Sie sich keine Sorgen, wenn Sie nicht alles (oder auch gar nichts) hiervon verstehen -- jede dieser Methoden und Eigenschaften lernen Sie in den nächsten Artikeln kennen. Was Sie allerdings mitnehmen sollten, ist ein Verständnis davon, was Sie mit XMLHttpRequest machen können. Beachten Sie, dass jede dieser Methoden und Eigenschaften etwas mit dem Versenden einer Anfrage oder dem Empfangen der Antwort zu tun hat. Wenn Sie sich jede der Methoden und Eigenschaften des XMLHttpRequest-Objekts ansehen, haben diese tatsächlich alle etwas mit dem einfachen Request-Response-Modell zu tun. Offensichtlich werden Sie also nichts über ein phantastisches, neues GUI-Objekt oder einen streng geheimen Ansatz zur User-Interaktion lernen; Sie werden mit einfachen Anfragen und einfachen Antworten arbeiten. Das mag sich nicht gerade aufregend anhören, aber der durchdachte Einsatz dieses Objekts kann Ihre Anwendungen von Grund auf verändern.

Die neue Einfachheit

Als Erstes benötigen Sie eine neue Variable und weisen dieser eine Instanz des XMLHttpRequest-Objekts zu. Das ist in JavaScript ziemlich einfach; Sie verwenden einfach das Schlüsselwort new mit dem Objektnamen, wie in Listing 1 gezeigt.


Listing 1. Ein neues XMLHttpRequest-Objekt erstellen


<script language="javascript" type="text/javascript">
var request = new XMLHttpRequest();
</script>

Das war doch nicht so schwierig, oder? Erinnern Sie sich daran, dass JavaScript keine Typisierung der Variablen verlangt, also brauchen so etwas wie in Listing 2 nicht (dort wird demonstriert, wie Sie das Objekt in Java erstellen würden).


Listing 2. Java-Pseudocode, um eine Instanz vom XMLHttpRequest zu erstellen


XMLHttpRequest request = new XMLHttpRequest();

Eine JavaScript-Variable erstellen Sie also mit var, geben ihr einen Namen (wie "request") und weisen ihr dann eine neue Instanz vom XMLHttpRequest zu. Nun sind wir bereit, das Objekt in unseren Funktionen zu nutzen.

Fehlerbehandlung

Im realen Leben können Dinge falsch laufen und dieser Code fängt solche Fehler nicht ab. Ein etwas besserer Ansatz ist, das Objekt zu erstellen und elegant abzufangen, wenn etwas schief läuft. Manche älteren Browser (ob Sie es glauben oder nicht, manche Leute verwenden tatsächlich noch alte Versionen des Netscape Navigators) unterstützen XMLHttpRequest zum Beispiel nicht und Sie müssen einen Weg besitzen, diese Benutzer davon in Kenntnis zu setzen, dass etwas fehlgeschlagen ist. Listing 3 zeigt, wie Sie das Objekt so erstellen, dass eine JavaScript-Nachricht ("Alert") erzeugt wird, wenn ein Fehler auftritt.


Listing 3. Erstellung eines XMLHttpRequest mit der Fähigkeit zur Fehlerbehandlung


<script language="javascript" type="text/javascript">
var request = false;
try {
  request = new XMLHttpRequest();
} catch (failed) {
  request = false;
}

if (!request)
  alert("Error initializing XMLHttpRequest!");
</script>

Vergewissern Sie sich, dass Sie jeden dieser Schritte verstanden haben:

  1. Eine neue Variable namens request erstellen und mit dem Wert false belegen. Diesen Wert werden Sie als Kriterium verwenden, ob das XMLHttpRequest-Objekt bereits erstellt worden ist.
  2. Einen try/catch-Block hinzufügen:
    1. Versuche ein XMLHttpRequest-Objekt zu erstellen.
    2. Falls das fehlschlägt (catch (failed)), sichern Sie ab, dass request immer noch den Wert false hat.
  3. Testen Sie, ob request noch den Wert false hat (wenn alles geklappt hat, ist das nicht der Fall).
  4. Falls es ein Problem gab (und request den Wert false hat), verwenden Sie eine JavaScript-Nachricht, um dem Benutzer zu sagen, dass es ein Problem gab.

Das war ziemlich einfach; es dauert viel länger, darüber zu lesen und zu schreiben, als die meisten JavaScript- und Web-Entwickler benötigen, um das zu verstehen. Wir haben jetzt also ein fehlersicheres Stück Code, welches ein XMLHttpRequest-Objekt erzeugt und Ihnen Bescheid gibt, wenn irgendetwas falsch gelaufen ist.

Und jetzt zu Microsoft

Das schaut ziemlich gut aus ... bis Sie diesen Code im Internet Explorer ausprobieren. Wenn Sie das machen, ist das Ergebnis so etwas wie in Abbildung 1.


Abbildung 1. Der Internet Explorer gibt einen Fehler aus.


Ist Microsoft ein Spielverderber?


Viel ist über Ajax und Microsofts steigendes Interesse und Präsenz in diesem Gebiet geschrieben worden. In der Tat soll die aktuellste Version des Internet Explorer -- Version 7.0, die Ende 2006 herauskommen soll -- eine direkte Unterstützung von XMLHttpRequest bieten, so dass Sie statt all diesem Msxml2.XMLHTTP-Code das Schlüsselwort new verwenden können. Aber freuen Sie sich nicht zu früh -- Sie müssen weiterhin auch ältere Browser unterstützen und der Cross-Browser-Code wird uns in näherer Zukunft erhalten bleiben.

Ganz klar, irgendwas funktioniert hier nicht; der Internet Explorer ist beileibe kein veralteter Browser und 70 Prozent der Welt verwendet ihn. Mit anderen Worten, für eine gute Web-Anwendung kommen Sie am Internet Explorer nicht vorbei! Also brauchen wir einen anderen Ansatz, wenn wir es mit dem Internet Explorer zu tun haben.

Microsoft unterstützt Ajax zwar, nennt seine Version des XMLHttpRequest aber etwas anders. Genauer gesagt verwendet er mehrere andere Namen. Wenn Sie eine neuere Version des Internet Explorer verwenden, dann müssen Sie ein Objekt namens Msxml2.XMLHTTP verwenden; einige ältere Versionen des Internet Explorer setzen dagegen Microsoft.XMLHTTP ein. Beide Objekt-Typen müssen Sie unterstützen (und natürlich weiterhin die Nicht-Microsoft Browser). In Listing 4 ist der Code dargestellt, mit dem auch Microsoft unterstützt wird.


Listing 4. Unterstützung von Microsoft-Browsern


<script language="javascript" type="text/javascript">
var request = false;
try {
  request = new XMLHttpRequest();
} catch (trymicrosoft) {
  try {
    request = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (othermicrosoft) {
    try {
      request = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (failed) {
      request = false;
    }
  }
}

if (!request)
  alert("Error initializing XMLHttpRequest!");
</script>

Bei diesen vielen geschweiften Klammern kann man schnell den Überblick verlieren, deshalb also Schritt für Schritt:

  1. Eine neue Variable namens request erstellen und ihr den Wert false zuweisen. Solange die Variable diesen Wert hat, wurde das XMLHttpRequest-Objekt noch nicht erstellt.
  2. Und im try/catch Block:
    1. Versuche, ein XMLHttpRequest-Objekt zu erstellen.
    2. Wenn das fehlschlägt (catch (trymicrosoft)):
      1. Versuche, ein Microsoft-kompatibles Objekt vom neueren Typ (Msxml2.XMLHTTP) zu erstellen.
      2. Wenn das fehlschlägt (catch (othermicrosoft)), versuche ein Microsoft-kompatibles Objekt vom älteren Typ (Microsoft.XMLHTTP) zu erstellen.
    3. Wenn das fehlschlägt (catch (failed)), stelle sicher, dass request immer noch den Wert false hat.
  3. Teste, ob request immer noch den Wert false hat (wenn alles richtig gelaufen ist, ist das nicht der Fall).
  4. Falls ein Problem aufgetreten ist (und request den Wert false hat), verwende eine JavaScript-Nachricht, um den Benutzer davon in Kenntnis zu setzen.

Machen Sie diese Änderungen in Ihrem Code und probieren sie es noch einmal mit dem Internet Explorer; Sie sollten das erstellte Formular sehen (und keine Fehlermeldung). Auf meinem Rechner sieht das Ergebnis wie in Abbildung 2 aus.


Abbildung 2. Nun funktioniert es auch mit dem Internet Explorer

Statisch versus dynamisch

Werfen Sie noch einmal einen Blick auf die Listings 1, 3 und 4. Der Code ist dort immer direkt innerhalb des script-Tags. Wenn JavaScript in dieser Form geschrieben und nicht innerhalb einer Methode oder Funktion definiert wird, nennt man das statisches JavaScript. Das bedeutet, dass der Code durchlaufen wird, bevor der User die Seite sieht. (Aus der Spezifikation ist nicht zu hundert Prozent präzise zu erkennen, wann der Code abläuft und Browser handhaben dieses unterschiedlich; fest steht jedoch, dass der Code abläuft, bevor der Benutzer mit der Seite interagieren kann.) Die meisten Ajax-Programmierer erstellen das XMLHttpRequest-Objekt als statisches JavaScript.

Nichtsdestotrotz können Sie Ihren Code auch innerhalb einer Methode schreiben wie in Listing 5 gezeigt.


Listing 5. Erstellung des XMLHttpRequest-Objekts innerhalb einer Methode


<script language="javascript" type="text/javascript">

var request;

function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (trymicrosoft) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (othermicrosoft) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = false;
      }
    }
  }

  if (!request)
    alert("Error initializing XMLHttpRequest!");
}
</script>

Wenn Sie das so programmieren, müssen Sie die Methode aufrufen, bevor Sie etwas mit Ajax machen können. Das wird dann also etwa so wie in Listing 6 aussehen.


Listing 6. Verwenden einer Methode, um ein XMLHttpRequest-Objekt zu erstellen


<script language="javascript" type="text/javascript">

var request;

function createRequest() {
  try {
    request = new XMLHttpRequest();
  } catch (trymicrosoft) {
    try {
      request = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (othermicrosoft) {
      try {
        request = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (failed) {
        request = false;
      }
    }
  }

  if (!request)
    alert("Error initializing XMLHttpRequest!");
}

function getCustomerInfo() {
  createRequest();
  // Jetzt etwas mit der request-Variable machen
}
</script>

Der einzige Nachteil dieses Codes -- und der Grund dafür, dass die meisten Ajax-Programmierer diesen Ansatz nicht verwenden -- ist, dass Fehlermeldungen verzögert werden. Nehmen wir mal an, Sie haben ein komplexes Formular mit zehn oder 15 Feldern, Auswahlboxen und so weiter, und Sie wollen Ajax-Code ausführen, wenn der Benutzer etwas in Feld 14 (also ziemlich weit unten im Formular) eingetragen hat. Dann wird getCustomerInfo() aufgerufen und versucht, ein XMLHttpRequest-Objekt zu erstellen und (für dieses Beispiel) dabei läuft etwas schief. JavaScript spuckt einen Fehler aus und teilt dem Benutzer wortreich mit, dass er diese Anwendung nicht nutzen kann. Allerdings hat der User bereits einiges an Zeit investiert, um das Formular zu füllen! Das ist ziemlich ärgerlich und wird sicher nicht dafür sorgen, dass der Benutzer noch einmal auf die Seite gelockt wird.

Falls Sie statisches JavaScript verwenden, wird der Benutzer dagegen über Fehler informiert, sobald er die Seite aufruft. Ist das auch ärgerlich? Vielleicht; wenn Benutzer sauer reagieren, weil die Web-Anwendung in ihrem Browser nicht läuft. Das ist jedoch erheblich besser, als wenn man diese Fehlermeldung erst bekommt, nachdem man zehn Minuten damit verbracht hat, das Formular auszufüllen. Schon aus diesem Grunde empfehle ich Ihnen den statischen Ansatz und den Benutzer früh über mögliche Probleme zu informieren.


Anfragen mit dem XMLHttpRequest senden

Sobald Sie das request-Objekt haben, können Sie mit dem Request/Response-Ablauf beginnen. Denken Sie daran, dass Sie das XMLHttpRequest-Objekt nur dazu benötigen, Anfragen zu stellen und Antworten zu erhalten. Alles andere -- Benutzer-Interface ändern, Bilder austauschen, sogar vom Server erhaltene Daten auszuwerten -- ist der Job von JavaScript, CSS oder anderem Code auf Ihren Seiten. Da der XMLHttpRequest nun also bereit ist, können wir eine Anfrage an den Server stellen.

Willkommen in der Sandbox

Das Sicherheitsmodell von Ajax folgt dem Sandbox-Prinzip. Das bedeutet, dass Ihr Ajax-Code (genauer gesagt das XMLHttpRequest-Objekt) nur Zugriffe auf die eigene Domain machen kann. In einem weiteren Artikel werden Sie eine Menge über Sicherheit und Ajax erfahren, doch für den Moment merken Sie sich einfach nur, dass der Code auf Ihrem lokalen Rechner nur Anfragen an serverseitige Skripte auf dem lokalen Recher stellen kann. Wenn der Ajax-Code also auf www.breakneckpizza.com läuft, muss er seine Anfragen an Skripte auf www.breakneckpizza.com richten.

Die Server-URL angeben

Als Erstes müssen Sie die URL des Servers bestimmen, mit dem die Verbindung aufgebaut werden soll. Das ist nichts Ajax-Spezifisches -- wie Sie eine URL aufbauen, sollten Sie inzwischen wissen -- doch es ist notwendig, um eine Verbindung zu öffnen. In den meisten Anwendungen werden Sie die URL aus einem statischen Anteil und variablen Daten aus dem Benutzer-Formular zusammenbauen. Als Beispiel zeigt Listing 7 etwas JavaScript-Code, der den Wert eines Telefonnummern-Feldes ausliest und daraus eine URL konstruiert.


Listing 7. Eine URL zusammenbauen


<script language="javascript" type="text/javascript">
   var request = false;
   try {
     request = new XMLHttpRequest();
   } catch (trymicrosoft) {
     try {
       request = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (othermicrosoft) {
       try {
         request = new ActiveXObject("Microsoft.XMLHTTP");
       } catch (failed) {
         request = false;
       }  
     }
   }

   if (!request)
     alert("Error initializing XMLHttpRequest!");

   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
   }
</script>

Hier sollten Sie über nichts stolpern. Als Erstes erstellt der Code eine neue Variable namens phone und weist ihr den Wert aus dem Formularfeld mit der ID "phone" zu. Listing 8 zeigt den XHTML-Code dieses Formulars mit dem Eingabefeld phone und seinem id-Attribut.


Listing 8. Das Break Neck Pizza-Formular


 <body>
  <p><img src="breakneck-logo_4c.gif" alt="Break Neck Pizza" /></p>
  <form action="POST">
   <p>Enter your phone number:
    <input type="text" size="14" name="phone" id="phone" 
           onChange="getCustomerInfo();" />
   </p>
   <p>Your order will be delivered to:</p>
   <div id="address"></div>
   <p>Type your order in here:</p>
   <p><textarea name="order" rows="6" cols="50" id="order"></textarea></p>
   <p><input type="submit" value="Order Pizza" id="submit" /></p>
  </form>
 </body>

Wenn der Benutzer seine Telefonnummer einträgt oder ändert, wird die getCustomerInfo()-Methode aus Listing 8 aufgerufen. Diese Methode holt sich die Nummer und verwendet sie, um die URL-Zeichenkette in der Variablen url zusammenzubauen. Denken Sie daran: Da Ajax dem Sandbox-Prinzip folgt und nur Daten von der gleichen Domain anfordern kann, benötigen Sie gar keinen Domain-Namen in Ihrer URL. In diesem Beispiel ist das Skript /cgi-local/lookupCustomer.php. An diesen Namen wird die Telefonnummer als GET-Parameter angehängt: "phone=" + escape(phone).

Falls Sie die escape()-Methode noch nie vorher gesehen haben: Diese wird verwendet, um alle Zeichen zu kodieren, die nicht als Klartext gesendet werden können. So werden zum Beispiel alle Leerzeichen in der Telefonnummer in %20 umgewandelt, damit diese Zeichen als Teil der URL gesendet werden können.

Sie können so viele Parameter hinzufügen, wie Sie benötigen. Wenn Sie also einen weiteren Parameter anhängen wollen, fügen Sie ihn einfach an die URL an und trennen die Parameter mit einem kaufmännischen Und-Zeichen (&). [Der erste Parameter wird vom Skript-Namen durch das Fragezeichen (?) getrennt.]

Die Anfrage beginnen

Öffnet open()?


Internet-Entwickler sind sich nicht einig, was genau die open()-Methode macht. Was sie nicht macht, ist wirklich eine Anfrage zu öffnen. Wenn Sie das Netzwerk und den Datentransfer zwischen der XHTML/Ajax-Seite und dem Ziel-Skript überwachen, würden Sie keinen Traffic sehen wenn die open()-Methode aufgerufen wird. Unklar ist, warum dieser Name gewählt wurde, aber es war in jedem Fall keine gute Wahl.

Mit der Ziel-URL können Sie die Anfrage konfigurieren, indem Sie den Wert an die open()-Methode Ihres XMLHttpRequest-Objekts übergeben. Diese Methode kann bis zu fünf Parameter erhalten:

  • request-type: der Typ der Anfrage. Übliche Werte sind GET oder POST, doch Sie können auch HEAD Anfragen senden.
  • url: die Ziel-URL.
  • asynch: Wenn Sie eine asynchrone Anfrage stellen wollen, muss der Wert auf true gesetzt sein, und auf false, wenn die Anfrage synchron verarbeitet werden soll. Dieser Parameter ist optional; der Standardwert ist true.
  • username: Falls eine Authentifizierung erforderlich ist, kann hier der Username angegeben werden. Dies ist ein optionaler Parameter ohne Default-Wert.
  • password: Falls eine Authentifizierung erforderlich ist, kann hier das Passwort angegeben werden. Dies ist ein optionaler Parameter ohne Default-Wert.

Üblicherweise werden Sie nur die ersten drei Parameter verwenden. Selbst wenn Sie eine asynchrone Anfrage stellen möchten, sollten Sie "true" als dritten Parameter angeben. Das ist zwar der Standardwert, doch der Code lässt sich leichter verstehen, wenn er in dieser Weise selbstdokumentierend ist.

Fügen Sie alles zusammen, so erhalten Sie eine Zeile, die wie in Listing 9 aussieht.


Listing 9. Die Anfrage beginnen


   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
   }

Wenn Sie erst einmal die URL haben, ist der Rest ziemlich trivial. Für die meisten Anfragen ist GET ausreichend. (Sie werden in späteren Artikeln sehen, dass es Situationen gibt, in denen POST besser ist.) Zusammen mit der URL haben Sie damit alles, um open() zu verwenden.

Gedanken zur (A)Synchronität

In einem späteren Artikel dieser Serie werde ich asynchronen Code ausführlich behandeln, bis dahin sollten Sie aber schon mal eine Vorstellung davon bekommen, warum der dritte Parameter in open() so wichtig ist. In einem normalen Request-Response-Modell -- denken Sie an Web 1.0 -- stellt der Client (Ihr Browser oder der Code, der auf Ihrem lokalen Rechner läuft) die Anfrage an den Server. Diese Anfrage wird synchron verarbeitet; der Client wartet also auf die Antwort des Servers. Während der Client wartet, sehen Sie mindestens eine dieser Wartemeldungen:

  • eine Sanduhr (in erste Linie unter Windows);
  • einen sich drehenden Ball ("spinning beachball") (normalerweise auf Mac Rechnern).
  • Die Anwendung friert im Wesentlichen ein und die Form des Cursors ändert sich.

Genau deswegen wirken Web-Anwendungen träge und langsam -- es fehlt an echter Interaktivität. Wenn Sie einen Button anklicken, wird die Anwendung im Grunde unbenutzbar, bis auf Ihre veranlasste Anfrage geantwortet wurde. Wenn die Anfrage einiges an Verarbeitung auf dem Server erforderlich macht, kann das eine nicht unerhebliche Wartezeit bedeuten (zumindest in der heutigen Mehrprozessor-DSL-Welt der Ungeduldigen).

Im Unterschied dazu wartet eine asynchrone Anfrage nicht, bis der Server antwortet. Sie senden eine Anfrage und die Anwendung läuft einfach weiter. Die Benutzer können immer noch Daten in das Formular eingeben, auf andere Buttons klicken, sogar das Formular ganz verlassen. Es gibt keinen "spinning beachball" oder eine sich drehende Sanduhr und die Anwendung friert auch nicht ein. Der Server verarbeitet die Anfrage im Hintergrund und wenn er fertig ist, teilt er das dem anfragenden Skript mit (wie, das werden Sie gleich noch sehen). Das Endergebnis ist eine Anwendung, die eben keinen trägen und langsamen Eindruck macht, sondern interaktiv und schnell reagierend wirkt. Dies ist nur eine Komponente vom Web 2.0, aber es ist eine sehr wichtige. All diese schicken GUI-Komponenten und Webdesign-Paradigmen können nicht über das langsame, synchrone Request-Response-Modell hinwegkommen.

Die Anfrage senden

Sobald Sie die Anfrage mit open() konfiguriert haben, sind Sie bereit, die Anfrage abzusenden. Zum Glück ist die Methode hierfür treffender benannt als open(); sie heißt ganz einfach send().

send() erwartet nur einen einzigen Parameter, den Inhalt, den die Methode senden soll. Bevor Sie jedoch hierüber lange nachdenken, rufen Sie sich ins Gedächtnis, dass Sie bereits Daten über die URL selbst senden:


var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);

Obwohl Sie Daten mittels send() abschicken können, geht das auch über die URL selbst. In der Tat ist es bei GET-Anfragen (die etwa 80 Prozent Ihrer typischen Ajax-Verwendung ausmachen werden) viel einfacher, die Daten in der URL zu senden. Wenn Sie damit anfangen, verschlüsselte Daten oder XML zu versenden, sollten Sie sich ansehen, wie man Inhalte mit send() verschickt (beides werden Themen für zukünftige Artikel sein). Wenn Sie keine Daten per send() übertragen wollen, setzen Sie einfach null als Argument dieser Methode. Für unser Beispiel in diesem Artikel ist es genau das, was wir benötigen (siehe Listing 10).


Listing 10. Anfrage senden


   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.send(null);
   }

Eine Callback-Methode angeben

Bis hierhin haben Sie nur wenig gemacht, was neu, revolutionär oder asynchron wirkt. Okay, dieses kleine Schlüsselwort "true" in der open()-Methode sorgt dafür, dass die Anfrage asynchron verarbeitet wird. Aber abgesehen davon sieht der Code ähnlich wie Java Servlets und JSP, PHP oder Perl aus. Was ist nun das große Geheimnis von Ajax und Web 2.0? Dieses Geheimnis rankt sich um eine einfache Eigenschaft von XMLHttpRequest namens onreadystatechange.

Machen Sie sich als Erstes noch einmal klar, was im Code abläuft (wenn nötig, schauen Sie sich Listing 10 noch einmal an). Eine Anfrage wird konfiguriert und dann gestellt. Da dies eine asynchrone Anfrage ist, wird die JavaScript-Methode (in unserem Beispiel getCustomerInfo()) nicht auf den Server warten. Der Code wird also weiter durchlaufen; in diesem Fall bedeutet das, dass die Methode endet und die Kontrolle an das Formular zurück gibt. Benutzer können weiter Informationen eingeben und die Anwendung wartet nicht auf den Server.

Das wirft jedoch eine interessante Frage auf: Was passiert, wenn der Server mit der Verarbeitung der Anfrage fertig ist? Die Antwort, zumindest mit dem jetzigen Code, lautet: Nichts! Das ist offensichtlich nicht gut, also braucht der Server irgendeine Form von Anweisung, was er tun soll, wenn er mit dem Verarbeiten der Anfrage fertig ist.

In JavaScript auf eine Funktion referenzieren


JavaScript ist eine lose typisierte Sprache und Sie können nahezu alles als Variable referenzieren. Wenn Sie also eine Funktion namens updatePage() deklarieren, behandelt JavaScript den Funktionsnamen als eine Variable. Mit anderen Worten, Sie können auf die Funktion in Ihrem Code als Variable updatePage referenzieren.

Und hier kommt die Eigenschaft onreadystatechange ins Spiel. Mit dieser Eigenschaft können Sie eine so genannte Callback-Methode angeben. Ein "Callback" ermöglicht Ihrem Server das Zurückkehren zum Code der Webseite. Es gibt auch ein Stück Kontrolle an den Server; wenn der Server mit der Anfrage fertig ist, schaut es im XMLHttpRequest-Objekt und dessen Eigenschaft onreadystatechange nach. Diejenige darin angegebene Methode wird danach aufgerufen. Das ist ein "Callback", da der Server das Zurückkehren auf die Webseite initiiert -- und zwar unabhängig davon, was auf der Webseite passiert. Zum Beispiel könnte er die Methode aufrufen, während der Benutzer auf seinem Stuhl sitzt und die Tastatur nicht berührt; aber genauso auch, wenn der Benutzer etwas eintippt, die Maus bewegt, scrollt, auf einen Button klickt, ... kurz, es ist egal, was der Benutzer macht.

Und hier kommt nun die Asynchronität zum Zuge: Der Benutzer bedient das Formular auf einem Level, während der Server auf einem anderen Level die Anfrage beantwortet und dann die Callback-Methode auslöst, die in der Eigenschaft onreadystatechange angegeben ist. Sie müssen also, wie in Listing 11 gezeigt, diese Methode im Code spezifizieren.


Listing 11. Setzen der Callback-Methode


   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

Geben Sie besonders darauf Acht, wo im Code diese Eigenschaft gesetzt wird -- das geschieht, bevor send() aufgerufen wird. Sie müssen diese Eigenschaft setzen, bevor die Anfrage gesendet wird, so dass der Server auf diese Eigenschaft zugreifen kann, wenn er mit der Verarbeitung der Anfrage fertig ist. Jetzt bleibt nur noch, die Methode updatePage() zu erstellen. Darum kümmern wir uns im letzten Abschnitt dieses Artikels.


Mit der Server-Antwort arbeiten

Sie haben Ihre Anfrage gestellt, der Benutzer arbeitet fröhlich mit dem Formular vor sich hin (während der Server die Anfrage verarbeitet), und nun ist der Server mit der Verarbeitung fertig. Der Server sieht in der Eigenschaft onreadystatechange nach, welche Methode er nun aufrufen soll. Sobald das passiert, läuft die Anwendung weiter wie jede andere, egal ob asynchron oder nicht. Mit anderen Worten, Sie brauchen keine speziellen Methoden schreiben, die dem Server antworten; Sie ändern einfach nur das Formular, leiten den Benutzer an eine andere URL weiter oder machen, was auch immer Sie als Aktion auf das Ergebnis des Servers tun wollen. In diesem Abschnitt geht es um die Reaktion auf die Serverantwort -- das direkte Ändern eines Teils des Formulars, welches der Benutzer sieht.

Callbacks und Ajax

Sie haben schon gesehen, wie Sie dem Server mitteilen, was er tun soll, sobald er fertig ist: Sie belegen die Eigenschaft onreadystatechange des XMLHttpRequest-Objekts mit dem Namen der Funktion, die aufgerufen werden soll. Dadurch ruft der Server automatisch diese Funktion auf, wenn er die Verarbeitung beendet hat. Sie brauchen sich auch nicht um irgendwelche Parameter dieser Methode Gedanken machen. Wir starten mit einer einfachen Methode wie in Listing 12.


Listing 12. Die Callback-Methode erstellen


<script language="javascript" type="text/javascript">
   var request = false;
   try {
     request = new XMLHttpRequest();
   } catch (trymicrosoft) {
     try {
       request = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (othermicrosoft) {
       try {
         request = new ActiveXObject("Microsoft.XMLHTTP");
       } catch (failed) {
         request = false;
       }  
     }
   }

   if (!request)
     alert("Error initializing XMLHttpRequest!");

   function getCustomerInfo() {
     var phone = document.getElementById("phone").value;
     var url = "/cgi-local/lookupCustomer.php?phone=" + escape(phone);
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

   function updatePage() {
     alert("Server is done!");
   }
</script>

Dies gibt einfach nur eine Nachricht aus, um Ihnen zu mitzuteilen, dass der Server fertig ist. Probieren Sie diesen Code in Ihrer Seite, speichern Sie sie und rufen Sie im Browser auf (das XHTML für dieses Beispiel finden Sie in Listing 8). Wenn Sie eine Telefonnummer eingeben und das Feld verlassen, sollten Sie einen JavaScript-Alert sehen (siehe Abbildung 3); doch wenn Sie auf OK klicken, erscheint das Fenster wieder ... und wieder.


Abbildung 3. Ajax gibt eine Nachricht aus

Abhängig von Ihrem Browser bekommen Sie diese Nachricht zwei, drei oder sogar vier Mal zu sehen, bevor das Formular damit aufhört. Was geht denn hier vor sich? Sie haben den HTTP-Verbindungsstatus nicht berücksichtigt, doch dieser ist eine wichtige Komponente im Request-Response-Ablauf.

HTTP-Verbindungsstatus

Etwas weiter oben habe ich gesagt, dass der Server die aufzurufende Methode in der Eigenschaft onreadystatechange des XMLHttpRequest-Objekts ausliest, sobald er mit der Anfrage fertig ist. Das ist richtig, aber nicht die komplette Wahrheit. Tatsächlich ruft er diese Methode jedes Mal auf, wenn sich der HTTP-Verbindungsstatus ändert. Was bedeutet das nun? Dazu müssen Sie erst verstehen, was es mit den Verbindungsstatus auf sich hat.

Ein HTTP-Verbindungsstatus gibt den Status der Anfrage an. Er wird verwendet, um herauszufinden, ob die Anfrage gestartet oder beantwortet ist oder ob das Request-Response-Modell vollendet worden ist. Der Status ist auch hilfreich, um zu testen, ob der Antworttext oder die Daten vom Server sicher ausgelesen werden dürfen. Sie müssen fünf verschiedene Status-Zustände in Ihren Ajax-Anwendungen kennen:

  • 0: Die Anfrage ist noch nicht initialisiert (bevor Sie open() aufrufen).
  • 1: Die Anfrage ist erstellt und initialisiert, aber noch nicht gesendet (bevor Sie send() aufrufen).
  • 2: Die Anfrage wurde gesendet und wird verarbeitet (Sie können den Antwort-Header auswerten).
  • 3: Die Anfrage wird verarbeitet; meistens steht ein Teil der Antwort schon zur Verfügung, doch der Server ist noch nicht mit der Antwort fertig.
  • 4: Die Anfrage ist abgeschlossen; die Antwort des Servers steht bereit.

Wie fast immer bei Cross-Browser-Angelegenheiten werden diese Verbindungsstatus-Zustände einigermaßen inkonsistent verwendet. Sie könnten erwarten, dass der Status von 0 nach 1 nach 2 nach 3 nach 4 wechselt, aber in der Realität ist das selten der Fall. Einige Browser geben nie eine 0 oder 1 zurück, sondern beginnen gleich mit 2, dann 3 und dann 4. Andere Browser melden alle Status-Zustände zurück. Wieder andere geben den Verbindungsstatus 1 gleich mehrfach zurück. Im letzten Abschnitt haben Sie gesehen, dass der Server mehrere Male updatePage() aufgerufen hat und jedes Mal wurde ein neues Alert-Fenster erzeugt -- wohl nicht ganz das, was Sie beabsichtigten!

Der einzige für Ajax wichtige Verbindungsstatus ist Status 4, der anzeigt, dass die Serverantwort vollständig ist und nun zuverlässig auf die Antwortdaten zugegriffen werden kann. Hierfür sorgt die erste Zeile aus Listing 13.


Listing 13. Den Verbindungsstatus abfragen


   function updatePage() {
     if (request.readyState == 4)
       alert("Server is done!");
   }

Diese Änderung fragt ab, ob der Server wirklich mit der Verarbeitung fertig ist. Probieren Sie diese Version des Ajax-Codes aus und Sie sollten die Nachricht nur einmal bekommen, also so, wie es gedacht war.

HTTP-Statuscodes

Trotz des scheinbaren Erfolgs vom Code in Listing 13 haben wir immer noch ein Problem: Was ist, wenn der Server zwar die Anfrage beantwortet und die Verarbeitung abschließt, doch dabei einen Fehler zurückmeldet? Denken Sie daran, dass Ihr serverseitiger Code sich darum kümmern sollte, egal ob er durch Ajax, JSP, ein normales HTML-Formular oder sonstigen Code aufgerufen wird. Er hat die üblichen webspezifischen Methoden, diese Informationen zurückzugeben. Und im Web können HTTP-Statuscodes mit ganz verschiedenen Dingen umgehen, die passieren können.

Wenn Sie zum Beispiel einen Tippfehler in der Ziel-URL haben, bekommen Sie einen 404-Fehlercode als Rückmeldung, dass die Seite nicht existiert. Dies ist nur einer von vielen Codes, den HTTP-Anfragen als Status erhalten können (in den Ressourcen finden Sie einen Link zu einer kompletten Liste von Statuscodes). Die Codes 401 und 403 für fehlende Berechtigungen sind auch ziemlich alltäglich. In all diesen Fällen werden die Statuscodes als Ergebnis von abgeschlossenen Anfragen zurückgegeben. Mit anderen Worten erfüllt der Server zwar die Anfrage (der HTTP-Verbindungsstatus ist also 4), aber er gibt möglicherweise nicht die Daten zurück, die der Client erwartet.

Zusätzlich zum Verbindungsstatus müssen Sie also den HTTP-Status überprüfen. Der sollte den Statuscode 200 haben, was bedeutet, dass alles erfolgreich verarbeitet wurde. Mit einem Verbindungsstatus von 4 und einem Statuscode von 200 sind Sie bereit, die Antwort des Servers zu verarbeiten und die Antwort sollte auch das sein, was Sie erwarten (und nicht ein Fehler oder sonstiges Problem). Fügen Sie daher einen weiteren Status-Check zu Ihrer Callback-Methode hinzu, wie in Listing 14 gezeigt.


Listing 14. Den HTTP-Statuscode überprüfen


   function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
   }

Damit das alles noch robuster wird, können Sie mit minimalem Aufwand auch noch andere Statuscodes abfragen; schauen Sie sich einmal die veränderte Version von updatePage() in Listing 15 an.


Listing 15. Einfache Fehlerabfragen hinzufügen


   function updatePage() {
     if (request.readyState == 4)
       if (request.status == 200)
         alert("Server is done!");
       else if (request.status == 404)
         alert("Request URL does not exist");
       else
         alert("Error: status code is " + request.status);
   }

Ändern Sie nun die URL in Ihrer getCustomerInfo() in eine nicht existierende URL ab und probieren Sie aus, was passiert. Sie sollten eine Alert-Nachricht sehen, die Ihnen mitteilt, dass die URL nicht existiert -- perfekt! Dies wird ganz bestimmt nicht jede mögliche Fehlerbedingung behandeln, doch es ist eine kleine Änderung, die 80 Prozent der üblichen Probleme einer typischen Web-Anwendung abfängt.

Den Antworttext auslesen

Da jetzt die Anfrage komplett abgeschlossen ist (das wissen Sie durch den Verbindungsstatus) und der Server eine normale Erfolgsmeldung (durch den Statuscode) zurückgegeben hat, können Sie sich nun den Antwortdaten widmen. Diese werden praktischerweise in der Eigenschaft responseText vom XMLHttpRequest-Objekt abgelegt.

Weitere Details -- etwa über das Format oder die Länge des Texts in responseText -- sind absichtlich unbestimmt gelassen. Der Server darf diesem Text so gut wie alles zuweisen. So könnte ein Skript durch Komma getrennte Werte zurückliefern, ein anderes den senkrechten Strich (|) als Trennzeichen benutzen und wieder ein anderes einfach eine lange Zeichenkette zurückgeben. Ganz wie es dem Server beliebt.

In unserem Beispiel aus diesem Artikel gibt der Server die letzte Bestellung des Kunden sowie dessen Adresse zurück, getrennt durch den senkrechten Strich. Sowohl die Bestellung als auch die Adresse verwenden wir, um die Werte von Formularfeldern zu setzen. Listing 16 enthält den Code der die Darstellung aktualisiert.


Listing 16. Die Serverantwort verarbeiten


   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "
");
} else alert("status is " + request.status); } }

Als Erstes wird der responseText ausgelesen und anhand des Trennzeichens mit der JavaScript-Methode split() in seine Teile zerlegt und als Array in der Variable response gespeichert. Der erste Wert -- die letzte Bestellung des Kunden -- ist im Array unter response[0] abgelegt und wird zum neuen Inhalt für das Feld mit der ID "order". Der zweite Wert im Array, response[1], ist die Kundenadresse und benötigt etwas mehr Aufmerksamkeit. Da die Adresszeilen mit dem normalen Zeilenseparator (das "\n" Zeichen) umgebrochen sind, müssen diese vom Code mit Zeilenumbrüchen im XHTML-Stil ersetzt werden, also mit <br />. Das wird mit der replace()-Funktion und einem regulären Ausdruck erreicht. Zum Schluss wird der so verarbeitete Text als inneres HTML vom div im HTML-Formular eingetragen. Das Ergebnis ist ein Formular, in dem plötzlich die Kundendaten aktualisiert sind, wie Sie in Abbildung 4 sehen können.


Abbildung 4. Das Break Neck Formular, nachdem es die Kundendaten erhalten hat

Bevor wir für heute zum Schluss kommen, sollten wir uns noch kurz eine weitere wichtige Eigenschaft von XMLHttpRequest namens responseXML ansehen. Diese Eigenschaft enthält (haben Sie es schon erraten?) die XML-Antwort für den Fall, dass der Server seine Antwort als XML geschickt hat. Diese XML-Antwort zu verarbeiten läuft ganz anders als die Verarbeitung von einfachem Text, und dazu gehört das Parsen, das Document Object Model (DOM) und ein paar weitere Dinge. In einem späteren Artikel werden Sie mehr über XML lernen. Doch da responseXML in Diskussionen häufig im Zusammenhang mit responseText auftaucht, sollten Sie schon einmal davon gehört haben. Bei den meisten einfachen Ajax-Anwendungen brauchen Sie nicht mehr als responseText, doch Sie werden über die Verarbeitung von XML in Ajax-Anwendungen auch noch mehr erfahren.


Resümee

Der XMLHttpRequest mag Sie jetzt etwas ermüdet haben -- selten habe ich einen ganzen Artikel über ein einziges Objekt gelesen, insbesondere über ein so einfaches. Jedoch werden Sie diesen Code immer und immer wieder auf jeder Seite und jeder Anwendung verwenden, die Sie mit Ajax schreiben. Um die Wahrheit zu sagen, es gibt noch einiges mehr über XMLHttpRequest zu erzählen. In den folgenden Artikeln werden Sie lernen, wie Sie auch POST (und nicht nur GET) in Ihren Anfragen verwenden, wie Sie den Kopf (Header) von Ihren Anfragen und den Antworten des Servers setzen und auslesen, wie Sie Anfragen kodieren und sogar XML im Request-Response-Modell verarbeiten.

Noch etwas später werden Sie ein paar beliebte Ajax-Toolkits kennenlernen. Im Wesentlichen nehmen Ihnen diese Toolkits die meisten der Code-Details ab, die wir in diesem Artikel gesehen haben, und machen die Ajax-Programmierung dadurch einfacher. Vielleicht fragen Sie sich, warum wir uns dann diesem ganzen Low-Level Code gewidmet haben, wenn es doch schon solche Toolkits gibt. Die Antwort ist: Es wäre außerordentlich schwierig, den Grund herauszufinden, wenn in Ihrer Anwendung etwas schief läuft, und Sie gar nicht wissen, was dort wirklich vor sich geht.

Also ignorieren (oder überfliegen) Sie diese Details nicht; wenn Ihr noch so tolles Toolkit einen Fehler erzeugt, werden Sie nicht wie der Ochs vorm Berg stehen und eine E-Mail an den Support schicken müssen. Wenn Sie verstehen, wie Sie XMLHttpRequest direkt verwenden, wird es für Sie ein Leichtes sei, selbst die seltsamsten Probleme zu debuggen und in Ordnung zu bringen. Toolkits sind eine prima Sache -- solange Sie sich nicht darauf verlassen, dass diese all Ihre Probleme lösen.

Machen Sie sich also mit XMLHttpRequest vertraut. Und wenn Sie schon Ajax-Code mit einem Toolkit erstellt haben, probieren Sie mal aus, den Code mit dem XMLHttpRequest-Objekt und seinen Eigenschaften und Methoden selbst zu schreiben. Das ist eine großartige Übung und wird Ihnen wahrscheinlich sehr dabei helfen, den Ablauf besser zu verstehen.

Im nächsten Artikel vertiefen wir dieses Objekt noch weiter indem wir uns ein paar der kniffligeren Eigenschaften (wie responseXML) ansehen. Außerdem werden wir Anfragen mit POST stellen und Daten in mehreren verschiedenen Formaten senden. Also ran an die Tastatur, probieren Sie alles aus und bis zum nächsten Mal.


Ressourcen

Weiterführende Literatur

Lesen Sie weiter: Teil 3 - Fortgeschrittene Anfragen und Antworten in Ajax >>

<< Zurück zu Teil 1 - Einführung in Ajax


Themen

Buchreihen

Special Interest

International Sites

O'Reilly China O'Reilly France O'Reilly USA O'Reilly Japan O'Reilly Taiwan