Ajax meistern, Teil 3: Fortgeschrittene Anfragen und Antworten in Ajax

 


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

14. Februar 2006

Deutsche Übersetzung: Claudia Nölker

Viele Web-Entwickler werden mit einfachen Anfragen und Antworten des Servers auskommen, doch für Entwickler, die Ajax meistern möchten, ist ein umfassendes Verständnis der HTTP-Statuscodes, des Verbindungsstatus und des XMLHttpRequest-Objekts notwendig. In diesem Artikel erklärt Brett McLaughlin die verschiedenen Statuscodes und wie Browser mit diesen umgehen. Er stellt dabei auch einige der weniger genutzen HTTP-Anfragen vor, die Sie mit Ajax machen können.

Im letzten Artikel dieser Serie, habe ich Ihnen das XMLHttpRequest-Objekt vorgestellt, das Herzstück einer Ajax-Anwendung, die Anfragen an eine serverseitige Komponente stellt und deren Antworten verarbeitet. Jede Ajax-Anwendung nutzt dieses XMLHttpRequest-Objekt, daher sollte Ihnen dieses sehr vertraut sein, damit Ihre Ajax-Anwendung wirklich gut läuft.

In diesem Artikel gehen wir über diese Grundlagen hinaus und sehen uns die drei Hauptteile dieses Anfrage-Objekts genauer an:

  • Der HTTP-Verbindungsstatus
  • Der HTTP-Statuscode
  • Die Typen der Anfragen, die Sie machen können

Jeder dieser Teile wird generell als Handlangerarbeit angesehen; als Folge davon sind nur wenig Details über diese Themen allgemein bekannt. Doch Statuscodes und Verbindungsstatus müssen Ihnen völlig geläufig sein, wenn Sie mehr als einfach nur etwas damit herumdaddeln möchten. Wenn mal etwas in Ihrer Anwendung schief läuft -- und es wird etwas schief gehen -- entscheidet das Wissen über den Verbindungsstatus, wie man eine HEAD-Anfrage stellt, oder was der Statuscode 400 bedeutet, darüber, ob Sie fünf Minuten debuggen oder fünf Stunden Frustration und Verwirrung erleben.

XMLHttpRequest oder XMLHttp: Ein anderer Name für's gleiche Kind


Microsoft™ und der Internet Explorer verwenden ein Objekt namens XMLHttp an Stelle des XMLHttpRequest-Objekts, welches von Mozilla, Opera, Safari und den meisten nicht von Microsoft kommenden Browsern eingesetzt wird. Der Einfachheit halber spreche ich immer XMLHttpRequest, wenn ich beide Typen meine. Dies hat sich im Internet so eingebürgert und ist auch im Einklang mit dem Vorhaben von Microsoft, in der Version 7.0 des Internet Explorers auch XMLHttpRequest als Name des Anfrage-Objekts zu verwenden. (Mehr hierzu können Sie im 2. Teil dieser Serie lesen.)

Als Erstes nun zum HTTP-Verbindungsstatus.

Ein genauerer Blick auf den HTTP-Verbindungsstatus

Aus dem letzten Artikel sollten Sie sich noch ins Gedächtnis rufen, dass das XMLHttpRequest-Objekt eine Eigenschaft namens readyState hat. Diese Eigenschaft stellt sicher, dass der Server eine Anfrage abgearbeitet hat. Üblicherweise nutzt eine Callback-Funktion die Ergebnisdaten des Servers, um ein Web-Formular oder eine Web-Seite zu aktualisieren. Listing 1 zeigt ein einfaches Beispiel hierfür (siehe auch den letzten Artikel dieser Serie in den Ressourcen).


Listing 1. Die Antwort des Servers in einer Callback-Funktion 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, "<br />");
     } else
       alert("status is " + request.status);
   }
 }

Dies ist definitiv die häufigste (und einfachste) Verwendung des Verbindungsstatus. Wie Sie schon aus der Nummer "4" erraten können, gibt es ein paar weitere Status (der letzte Artikel enthielt eine Liste hiervon -- siehe Ressourcen):

  • 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 die Kopfdaten der Antwort 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.

Wenn Sie sich tiefer in die Ajax-Programmierung einarbeiten wollen, müssen Sie diese Status nicht nur kennen, sondern auch wissen, wann sie eintreten und wie Sie sie verwenden können. Zuallererst sollten Sie lernen, zu welchem Zeitpunkt einer Anfrage welcher Verbindungsstatus vorliegt. Leider ist das wenig intuitiv und es gibt ein paar Besonderheiten zu beachten.

Verschwundene Verbindungsstatus

Der erste Verbindungsstatus, die Eigenschaft readyState hat dort den Wert 0 (readyState == 0), repräsentiert eine nicht initialisierte Anfrage. Sobald Sie open() für Ihr Anfrage-Objekt aufrufen, wird diese Eigenschaft auf 1 gesetzt. Da Sie nahezu immer gleich nach der Initialisierung open() aufrufen, werden Sie readyState == 0 nur selten sehen. Zudem ist der nicht-initialisierte Verbindungsstatus in echten Anwendungen so gut wie nutzlos.

Der Vollständigkeit halber sehen Sie in Listing 2, wie Sie den Verbindungsstatus abfragen, wenn er auf 0 gesetzt ist.


Listing 2. Der Verbindungsstatus 0


   function getSalesData() {
     // Ein Anfrage-Objekt erstellen
     createRequest();  
     alert("Ready state is: " + request.readyState);

     // Die Anfrage initialisieren
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

In diesem einfachen Beispiel ist getSalesData() die Funktion, die Ihre Webseite aufruft, um eine Anfrage zu starten (etwa wenn ein Button angeklickt wird). Achten Sie darauf, dass der Verbindungsstatus überprüft wird, bevor open() aufgerufen wird. Abbildung 1 zeigt das Ergebnis wenn Sie diese Anwendung laufen lassen.


Abbildung 1. Der Verbindungsstatus 0
Der Verbindungsstatus 0

Wenn 0 gleich 4 ist


Für den Fall, dass mehrere JavaScript-Funktionen das gleiche Anfrage-Objekt verwenden, stellt es sich als nicht ausreichend heraus, einfach nur den Verbindungsstatus 0 abzufragen um zu erfahren ob das Objekt schon benutzt wird. Da readyState == 4 eine abgeschlossene Anfrage bedeutet, werden Sie häufiger Anfrage-Objekte vorfinden, die nicht genutzt werden, obwohl der Verbindungsstatus immer noch 4 ist -- die Daten des Servers wurden verwendet, aber nichts hat den Verbindungsstatus seitdem geändert. Es gibt eine Funktion abort(), die die Anfrage abbricht, aber sie ist nicht für diesen Zweck gedacht. Wenn Sie wirklich mehrere Funktionen benötigen, dann könnte es besser sein, für jede Funktion ein eigenes Anfrage-Objekt zu erstellen und zu verwenden, als ein gemeinsames Objekt in mehreren Funktionen einzusetzen.

Zugegeben, hiermit können Sie nicht so viel anfangen; es wird nur selten vorkommen, dass Sie gewährleisten müssen, dass open() noch nicht aufgerufen wurde. Die einzige Verwendung für diesen Verbindungsstatus in einer echten Ajax-Anwendung ist, mehrere Anfragen in verschiedenen Funktionen mit dem selben XMLHttpRequest-Objekt machen zu wollen. In diesem (eher ungewöhnlichen) Fall könnten Sie überprüfen, dass das Objekt noch nicht initialisiert ist (readyState == 0), bevor Sie neue Anfragen starten. Das stellt dann sicher, dass keine andere Funktion das Objekt zur gleichen Zeit nutzt.

Den Verbindungsstatus während einer Anfrage beobachten

Außer dem Verbindungsstatus 0 sollte Ihr Anfrage-Objekt während einer typischen Anfrage und Antwort jeden der weiteren Status annehmen, bis es den Wert 4 hat. Wenn der Status 4 ist, kommt die Code-Zeile if (request.readyState == 4) aus der Callback-Funktion zum Zuge; diese Zeile stellt sicher, dass der Server fertig ist und die Webseite nun mit dem Ergebnis ordentlich aktualisiert werden kann.

Diesen Prozess zu beobachten ist schon fast trivial. Anstatt den Code in der Callback-Funktion nur aufzurufen, wenn der Verbindungsstatus 4 ist, geben Sie einfach jedes Mal den Verbindungsstatus aus, wenn Ihre Callback-Funktion aufgerufen wird. In Listing 3 sehen Sie ein Code-Beispiel, das dieses macht.


Listing 3. Den Verbindungsstatus überprüfen


   function updatePage() {
     // Den aktuellen Verbindungsstatus ausgeben
     alert("updatePage() called with ready state of " + request.readyState);
   }

Wenn Sie sich nicht sicher sind, wie Sie dieses zum Laufen bringen, hier ein paar Tipps: Sie müssen eine Funktion erstellen, die durch Ihre Webseite aufgerufen wird und die dann die Anfrage an eine serverseitige Komponente schickt. (So eine Funktion ist in Listing 2 gezeigt und auch in den Beispielen in den ersten beiden Artikeln dieser Serie.) Achten Sie darauf, beim Initialisieren als Callback-Funktion updatePage() anzugeben. Dazu setzen Sie einfach die Eigenschaft onreadystatechange auf den Wert updatePage.

Mit diesem Code wird prima erkennbar, was genau onreadystatechange bedeutet -- jedes Mal, wenn der Verbindungsstatus der Anfrage sich ändert, wird updatePage() aufgerufen und Sie sehen eine JavaScript-Meldung ("Alert"). Abbildung 2 zeigt das Ergebnis des Aufrufs für den Fall, dass der Verbindungsstatus den Wert 1 hat.


Abbildung 2. Der Verbindungsstatus 1
Der Verbindungsstatus 1

Probieren Sie das selbst einmal aus. Fügen Sie den Code in Ihre Webseite hinzu und aktivieren dann den Event-Handler (indem Sie auf einen Button klicken, mit Tab aus einem Feld springen oder auf eine andere Weise eine Anfrage auslösen). Ihre Callback-Funktion wird mehrfach durchlaufen -- jedes Mal, wenn der Verbindungsstatus sich ändert -- und Sie sehen einen Alert für jeden Verbindungsstatus. Auf diese Weise können Sie die Anfrage durch alle Status sehr gut verfolgen.

Inkonsistenzen bei Browsern

Sobald Sie ein grundlegendes Verständnis dieses Prozesses haben, testen Sie das einmal mit mehreren verschiedenen Browsern. Sie werden ein paar Inkonsistenzen bemerken, wie diese Verbindungsstatus behandelt werden. Im Firefox 1.5 sehen Sie zum Beispiel diese Verbindungsstatus:

  • 1
  • 2
  • 3
  • 4

Das sollte Sie nicht wirklich überraschen, da jeder Verbindungsstatus der Anfrage vorkommt. Wenn Sie jedoch die gleich Anwendung mit dem Safari-Browser aufrufen sollten Sie etwas Interessantes sehen -- oder wohl eher nicht sehen. Diese Verbindungsstatus zeigt der Safari 2.0.1:

  • 2
  • 3
  • 4

Safari lässt tatsächlich den ersten Verbindungsstatus aus und gibt es keine vernünftige Erklärung dafür -- das ist einfach eine Eigenheit des Safari-Browsers. Das zeigt uns einen wichtigen Punkt: Auch wenn es eine gute Idee ist, den Verbindungsstatus 4 abzuwarten, bevor wir die Daten des Servers verwenden, macht das keinen Sinn für alle Verbindungsstatus. Wenn Sie Code schreiben, der auf jeden Zwischenstatus angewiesen ist, wird das unweigerlich zu Problemen auf unterschiedlichen Browsern führen.

Noch schlimmer wird es, wenn wir Opera 8.5 verwenden, um die Verbindungsstatus anzuzeigen:

  • 3
  • 4

Der Internet Explorer wiederum antwortet mit diesen Verbindungsstatus:

  • 1
  • 2
  • 3
  • 4

Wenn Ihnen eine Anfrage mal Schwierigkeiten bereitet, ist dies die allererste Stelle an der Sie nach Problemen fahnden sollten. Fügen Sie einen Alert hinzu, der den Verbindungsstatus ausgibt, so dass Sie sicherstellen können, dass alles normal abläuft. Noch besser ist ein Test mit dem Internet Explorer und dem Firefox -- dabei durchläuft die Anfrage alle vier Verbindungsstatus und Sie können jeden davon einzeln überprüfen.

Als Nächstes nun zur Antwort des Servers.

Die Antwortdaten unter der Lupe

Sobald Sie die unterschiedlichen Verbindungsstatus verstanden haben, die während einer Anfrage auftreten, sind Sie bereit dazu, den nächsten wichtigen Teil des XMLHttpRequest-Objekts anzusehen -- die responseText Eigenschaft. Aus dem letzten Artikel wissen Sie schon, dass diese Eigenschaft genutzt wird, die Daten des Servers zu bekommen. Nachdem der Server mit der Verarbeitung der Anfrage fertig ist, steckt er alle Daten, die zurückgegeben werden sollen, in die Eigenschaft responseText des Anfrage-Objekts. Danach kann Ihre Callback-Funktion diese Daten verwenden, wie in Listing 1 und Listing 4 gezeigt.


Listing 4. Die Antwort des Servers verwenden


   function updatePage() {
     if (request.readyState == 4) {
       var newTotal = request.responseText;
       var totalSoldEl = document.getElementById("total-sold");
       var netProfitEl = document.getElementById("net-profit");
       replaceText(totalSoldEl, newTotal);

       /* Den neuen Gewinn berechnen */
       var boardCostEl = document.getElementById("board-cost");
       var boardCost = getText(boardCostEl);
       var manCostEl = document.getElementById("man-cost");
       var manCost = getText(manCostEl);
       var profitPerBoard = boardCost - manCost;
       var netProfit = profitPerBoard * newTotal;

       /* Den Gewinn im Umsatz-Formular aktualisieren */
       netProfit = Math.round(netProfit * 100) / 100;
       replaceText(netProfitEl, netProfit);
     }

Listing 1 ist ziemlich einfach gehalten; Listing 4 ist schon etwas komplizierter. Beide überprüfen jedoch zuerst den Verbindungsstatus und lesen dann den Wert (oder die Werte) aus der responseText-Eigenschaft aus.

Die Antwortdaten während der Anfrage ansehen

Ebenso wie der Verbindungsstatus ändert sich auch der Wert der responseText-Eigenschaft im Laufe des Request-Response-Zyklus'. Um dieses zu beobachten, können Sie den Code aus Listing 5 verwenden, um die Antwortdaten der Anfrage zusätzlich zum Verbindungsstatus zu sehen.


Listing 5. Die responseText-Eigenschaft ausprobieren


   function updatePage() {
     // Ausgabe des aktuellen Verbindungsstatus und der Antwortdaten
     alert("updatePage() called with ready state of " + request.readyState +
           " and a response text of '" + request.responseText + "'");
     }

Öffnen Sie Ihre Web-Anwendung nun in einem Browser und aktivieren Sie Ihre Anfrage. Am besten nutzen Sie dafür entweder Firefox oder den Internet Explorer, da die Abfrage in diesen beiden Browsern alle möglichen Verbindungsstatus durchläuft. Beim Verbindungsstatus 2 ist die responseText-Eigenschaft zum Beispiel noch undefiniert (siehe Abbildung 3); wenn Sie gleichzeitig die JavaScript Konsole geöffnet haben, sehen Sie dort auch einen Fehler.


Abbildung 3. Antwortdaten bei Verbindungsstatus 2
Antwortdaten bei Verbindungsstatus 2

Beim Verbindungsstatus 3 hat der Server dagegen schon einen Wert in die Eigenschaft responseText geschrieben, zumindest in diesem Beispiel (siehe Abbildung 4).


Abbildung 4. Antwortdaten bei Verbindungsstatus 3
Antwortdaten bei Verbindungsstatus 3

Sie werden bemerken, dass die Antwortdaten in Verbindungsstatus 3 von Skript zu Skript, Server zu Server und Browser zu Browser verschieden sind. Dennoch bleibt diese Abfrage ungemein hilfreich beim Debuggen Ihrer Anwendung.

Unversehrte Daten bekommen

Alle Dokumentationen und Spezifikationen beharren darauf, dass die Antwortdaten erst im Verbindungsstatus 4 zur weiteren Verwendung sicher sind. Glauben Sie mir, es kommt nur äußerst selten vor, dass die Daten nicht schon im Verbindungsstatus 3 in der responseText-Eigenschaft vorliegen. Dennoch sollten Sie sich nicht darauf verlassen -- genau dann, wenn Sie Code schreiben, der die Antwortdaten schon im Verbindungsstatus 3 ausliest, kommen die Daten garantiert unvollständig an.

Eine bessere Idee ist es dagegen schon, dem Benutzer etwas Feedback zu geben, sobald der Verbindungsstatus 3 ist und eine Antwort bald zu erwarten ist. Hierzu eine Funktion wie alert() zu nutzen, ist natürlich keine gute Idee -- Ajax einzusetzen und den Benutzer dann mit einer Alert-Nachricht auszubremsen, ist ziemlich kontraproduktiv -- doch Sie könnten ein Feld im Formular oder auf der Seite aktualisieren, wenn der Verbindungsstatus sich ändert. Setzen Sie zum Beispiel die Breite einer Fortschrittsanzeige auf 25 Prozent beim Verbindungsstatus 1, auf 50 Prozent beim Wert 2, auf 75 Prozent beim Verbindungsstatus 3 und dann auf 100 Prozent wenn der Verbindungsstatus den Wert 4 hat.

Natürlich ist dieser Ansatz zwar clever, aber leider auch browserabhängig. Mit dem Opera-Browser werden Sie die ersten beiden Verbindungsstatus nie erreichen, und Safari unterschlägt den ersten (Verbindungsstatus 1). Aus diesem Grunde überlasse ich Ihnen den Code dafür als Übung anstatt ihn in diesem Artikel aufzunehmen.

Es wird Zeit, sich die Statuscodes näher anzusehen.

Ein genauerer Blick auf die HTTP-Statuscodes.

Mit den Verbindungsstatus und den Antwortdaten im Repertoir Ihrer Ajax-Programmiertechniken sind Sie nun bereit, den nächsten Vollkommenheitsgrad Ihrer Ajax-Anwendungen zu erreichen -- den Einsatz von HTTP-Statuscodes. Diese Codes sind nichts Neues für Ajax. Es gibt sie schon so lange, wie es das Internet gibt. Wahrscheinlich haben Sie schon mehrere davon in Ihrem Web-Browser gesehen:

  • 401: Unauthorized (Authentifizierung ungültig)
  • 403: Forbidden (fehlende Berechtigungen)
  • 404: Not Found (Ressource nicht gefunden)

Und es gibt noch einige mehr. (In den Ressourcen finden Sie eine komplette Liste.) Um noch mehr Kontrolle und Reaktionsmöglichkeit (und insbesondere robustere Fehlerbehandlung) über Ihre Ajax-Anwendungen zu haben, müssen Sie die Statuscodes der Abfrage überprüfen und entsprechend agieren.

200: Alles ist okay

In vielen Ajax-Anwendungen werden Sie eine Callback-Funktion vorfinden, die den Verbindungsstatus überprüft und dann mit der Verarbeitung der Antwortdaten weitermacht, so wie in Listing 6.


Listing 6. Callback-Funktion, die den Statuscode ignoriert


   function updatePage() {
     if (request.readyState == 4) {
       var response = request.responseText.split("|");
       document.getElementById("order").value = response[0];
       document.getElementById("address").innerHTML =
         response[1].replace(/\n/g, "<br />");
     }
   }

Das stellt sich als ein etwas kurzsichtiger und fehleranfälliger Ansatz der Ajax-Programmierung heraus. Wenn ein Skript eine Authentifizierung erwartet und Ihre Anfrage keine gültigen Login-Daten bereitstellt, wird der Server einen Fehlercode wie 403 oder 401 zurückgeben. Der Verbindungsstatus wird jedoch den Wert 4 haben, da der Server die Anfrage beantwortet hat (selbst wenn die Antwort nicht die war, die Sie auf Ihre Anfrage erwartet haben). Das Ergebnis ist, dass der Benutzer keine gültigen Daten, sondern stattdessen womöglich eine grässliche Fehlermeldung bekommt, wenn JavaScript versucht, auf nicht existente Serverdaten zuzugreifen.

Mit nur minimalem Aufwand können wir gewährleisten, dass der Server die Anfrage nicht nur beantwortet hat, sondern auch als Statuscode "Alles ist okay" zurückgegeben hat. Der numerische Wert dafür ist 200 und wird in der status-Eigenschaft des XMLHttpRequest-Objekts gespeichert. Um also sicherzustellen, dass der Server nicht nur mit der Anfrage fertig ist, sondern auch den Okay-Status zurückgemeldet hat, fügen Sie Ihrer Callback-Funktion wie in Listing 7 gezeigt eine weitere Prüfung hinzu.


Listing 7. Auf den richtigen Statuscode überprüfen


   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, "<br />");
       } else
         alert("status is " + request.status);
     }
   }

Durch diese wenigen neuen Code-Zeilen haben Sie sichergestellt, dass der Benutzer eine (zweifelhaft) hilfreiche Nachricht bekommt, wenn etwas schief gelaufen ist, anstatt eine Seite mit kaputten Daten ohne jegliche Erklärung zu sehen.

Weiterleitung und Umleitung

Bevor ich im Detail über Fehler spreche, sollten wir uns kurz einem Thema zuwenden, welches Ihnen keine Kopfschmerzen bereiten wird, wenn Sie Ajax verwenden -- Umleitungen. In den HTTP-Statuscodes ist dies die 300er Familie, zu denen auch diese Codes gehören:

  • 301: Moved permanently (gefunden unter dauerhaft neuer Adresse)
  • 302: Found (die Anfrage wurde zu einer neuen URL/URI weitergeleitet)
  • 305: Use Proxy (die Anfrage muss einen Proxy verwenden, um die URL/URI zu erreichen)

Ajax-Programmierer brauchen sich aus zwei Gründen nicht um Umleitungen zu kümmern:

  • Zunächst einmal sind Ajax-Anwendungen fast immer für ein spezifisches serverseitiges Skript, Servlet oder eine Anwendung geschrieben. Dass diese Komponente ohne Ihr (des Programmierers) Wissen verschwindet oder an eine andere Stelle verschoben wird, ist ziemlich unwahrscheinlich. Meistens werden Sie wissen, dass die Ressource nun an anderer Stelle zu finden ist (weil Sie sie selbst verschoben haben oder Sie sie haben verschieben lassen), ändern in diesem Fall dann die URL in Ihrer Anfrage und bekommen diesen Statuscode nie gemeldet.
  • Und noch ein relevanterer Grund ist: Ajax-Anwendungen und -Anfragen folgen dem Sandbox-Prinzip. Das bedeutet, dass die Anfragen von einer Webseite zu einem Ziel auf der gleichen Domäne gerichtet sein müssen. Eine Webseite auf ebay.de kann also keine Anfragen im Ajax-Stil auf ein Skript in der Domäne amazon.de machen; Ajax-Anwendungen auf ibm.com können keine Anfragen an Servlets auf netbeans.org stellen.

Als Folge hiervon können Ihre Anfragen nicht auf einen anderen Server umgeleitet werden, ohne einen Sicherheitsfehler hervorzurufen. In diesem Fall bekommen Sie überhaupt keinen Statuscode. Sie erhalten normalerweise nur einen JavaScript-Fehler in der Debug-Konsole. Wenn Sie also an die vielen Statuscodes denken, können Sie die Umleitungscodes weitgehend ignorieren.

Grenz- und Härtefälle


An diesem Punkt mögen Programmieranfänger sich fragen, was das ganze große Aufhebens hier soll. Natürlich ist es richtig, dass weniger als fünf Prozent der Ajax-Anfragen die Verarbeitung von Verbindungsstatus 2 und 3 oder Statuscodes wie 403 erfordern (und tatsächlich sind es wohl eher 1 Prozent oder weniger). Diese Grenzfälle ("edge cases") sind jedoch wichtig -- ungewöhnliche Konstellationen kommen immer wieder vor. Auch wenn sie ungewöhnlich sind, machen genau diese Grenzfälle etwa 80 Prozent der Frustration der Benutzer aus!

Typische Benutzer vergessen die Hunderte von Malen, die die Anwendung korrekt gearbeitet hat, erinnern sich jedoch genau daran, als sie nicht funktionierte. Wenn Sie diese Grenzfälle -- und Härtefälle -- reibungslos bewältigen, werden Sie zufriedene Benutzer haben, die auf Ihre Seite zurückkehren.

Fehler

Nun da Sie sich um den Statuscode 200 gekümmert haben und wissen, dass Sie die 3xx-Statuscodes weitgehend ignorieren können, bleibt als weitere Gruppe die 400er-Familie, die verschiedene Fehlertypen anzeigt. Schauen Sie sich Listing 7 noch einmal an. Dort werden Fehler zwar behandelt, doch der Benutzer bekommt nur eine sehr allgemein gehaltene Fehlernachricht. Das ist schon mal ein Schritt in die richtige Richtung, jedoch ziemlich nutzlos, was Informationen über die Ursache des Fehlers betrifft.

Als Erstes wollen wir eine Rückmeldung für fehlende Seiten einbauen. Das sollte in einem Produktivsystem zwar nicht häufig vorkommen, doch es ist nicht ungewöhnlich zu testen, ob das Skript an eine anderere Stelle verschoben wurde oder ein Programmierer eine falsche URL eingetragen hat. Wenn Sie solche 404-Fehler abfangen, helfen Sie verwirrten Benutzern und Programmierern sehr. Wenn zum Beispiel ein Skript auf dem Server verschoben wurde und Sie den Code in Listing 7 verwenden, sehen Sie die wenig aussagekräftige Fehlermeldung wie in Abbildung 5.


Abbildung 5. Allgemeine Behandlung von Fehlern
Allgemeine Behandlung von Fehlern

Der Benutzer hat keine Chance herauszufinden, ob das Problem die Authentifizierung, ein fehlendes Skript (was hier der Fall ist), ein Benutzerfehler oder irgendetwas im Code ist, was den Fehler hervorgerufen hat. Mit ein paar einfachen Zusätzen im Code wird die Fehlermeldung gleich viel spezifischer. Schauen Sie sich das Listing 8 an, welches fehlende Skripte und Authentifizierungsfehler abfängt und eine aussagekräftige Fehlermeldung ausgibt.


Listing 8. Den Statuscode auf Richtigkeit überprüfen


   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, "<br />");
       } else if (request.status == 404) {
         alert ("Requested URL is not found.");
       } else if (request.status == 403) {
         alert("Access denied.");
       } else
         alert("status is " + request.status);
     }
   }

Dies ist immer noch ziemlich einfach gehalten, doch es bietet zusätzliche Informationen. Die Abbildung 6 zeigt den gleichen Fehler wie Abbildung 5, doch dieses Mal gibt die Fehlermeldung dem Benutzer oder Programmierer wichtige Hinweise über die Fehlerursache.


Abbildung 6. Aussagekräftige Fehlerbehandlung
Specific error handling

In Ihren eigenen Anwendungen möchten Sie möglicherweise den Benutzernamen und das Passwort nicht zeigen, wenn die Authentifizierung fehlschlägt, sondern stattdessen eine Fehlermeldung. In ähnlicher Form können Sie auch weitere Fehler abfangen, zum Beispiel fehlende Skripte und andere 4xx-Fehler (wie 405 für eine nicht akzeptierte Anfragemethode, wenn eine HEAD-Anfrage gesendet wurde, oder den Statuscode 407, der auf eine notwendige Proxy-Authentifizierung hinweist). Wie auch immer Sie sich entscheiden, Sie müssen immer bei der Abfrage des Statuscodes der Serverantwort ansetzen.

Weitere Anfragetypen

Wenn Sie das XMLHttpRequest-Objekt wirklich komplett beherrschen wollen, sollten Sie auch die HEAD-Anfragen zu Ihrem Repertoir hinzufügen. In den vorherigen zwei Artikeln habe ich Ihnen gezeigt, wie Sie GET-Anfragen machen; in einem kommenden Artikel werden Sie alles über das Senden von Daten zum Server mit der POST-Anfrage lernen. Für den Zweck der besseren Fehlerbehandlung und dem Sammeln von Informationen sollten Sie auch lernen, wie man HEAD-Anfragen macht.

Die Anfrage stellen

Eine HEAD-Anfrage zu stellen, ist im Wesentlichen ziemlich trivial; Sie rufen ganz einfach nur die open()-Methode mit "HEAD" anstelle von "POST" als erstem Parameter auf, so wie in Listing 9 gezeigt.


Listing 9. Eine HEAD-Anfrage mit Ajax machen


   function getSalesData() {
     createRequest();
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("HEAD", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

Wenn Sie auf diese Weise eine HEAD-Anfrage machen, gibt der Server keine solche Antwort zurück, wie Sie bei einer GET- oder POST-Anfrage bekämen. Statt dessen gibt der Server nur die Kopfdaten (Headers) der Ressource zurück, hierin enthalten sind auch der Zeitpunkt der letzten Inhaltsänderung, ob die angefragte Ressource überhaupt existiert und noch eine ganze Reihe weitere interessante Informationen. Einige davon können Sie nutzen, um mehr über die Ressource herauszufinden, bevor der Server die Anfrage verarbeitet und die Ressource zurückgibt.

Das Einfachste, was Sie mit einer Anfrage wie dieser tun können, ist eine bloße Ausgabe aller Kopfdaten der Antwort. Dadurch bekommen Sie ein Gefühl dafür, welche Informationen durch eine solche HEAD-Anfrage zur Verfügung stehen. Listing 10 zeigt eine einfache Callback-Funktion, um alle Kopfdaten der Antwort auf eine HEAD-Anfrage auszugeben.


Listing 10. Alle Kopfdaten der Antwort auf eine HEAD-Anfrage ausgeben


   function updatePage() {
     if (request.readyState == 4) {
       alert(request.getAllResponseHeaders());
     }
   }

In Abbildung 7 sehen Sie die Ausgabe aller Kopfdaten der Antwort auf eine einfache Ajax-Anwendung, die eine HEAD-Anfrage an den Server stellt.


Abbildung 7. Kopfdaten der Antwort auf eine HEAD-Anfrage
Kopfdaten der Antwort auf eine HEAD-Anfraget

Auf diese Kopfdaten (über den Server als auch über den Inhalt) können Sie auch einzeln zugreifen, um zusätzliche Informationen oder Funktionalitäten innerhalb der Ajax-Anwendung zu bieten.

Eine URL prüfen

Sie haben schon gesehen, wie Sie einen 404-Fehler abfangen, wenn die URL nicht existiert. Wenn sich dies als häufiges Problem herausstellt -- etwa weil ein bestimmtes Skript oder Servlet ziemlich oft offline ist -- möchten Sie die URL vielleicht prüfen, bevor Sie die komplette GET- oder POST-Anfrage stellen. Zu diesem Zweck stellen Sie eine HEAD-Anfrage und prüfen dann in Ihrer Callback-Funktion, ob ein 404-Fehler aufgetreten ist; Listing 11 zeigt ein einfaches Codebeispiel hierfür.


Listing 11. Prüfen, ob eine URL existiert


   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         alert("URL exists");
       } else if (request.status == 404) {
         alert("URL does not exist.");
       } else {
         alert("Status is: " + request.status);
       }
     }
   }

Um ehrlich zu sein, dies hat wenig Wert. Der Server muss auf die Anfrage antworten und die recht lange Antwort mit den Kopfdaten zusammenstellen, also sparen Sie keine Verarbeitungszeit hierdurch. Außerdem kostet es genauso viel Zeit, die HEAD-Anfrage zu machen, um zu prüfen, ob die URL existiert, wie die eigentliche GET- oder POST-Anfrage mit einer Fehlerbehandlung wie in Listing 7 auch in Anspruch nimmt. Dennoch ist es nicht verkehrt zu wissen, was verfügbar ist; Sie wissen ja nie, wann Ihnen ein Gedankenblitz kommt und Sie eine HEAD-Anfrage gut verwenden können!

Nützliche HEAD-Anfragen

Einen Bereich, in dem Sie eine HEAD-Anfrage sinnvoll einsetzen können, ist die Abfrage der Inhaltslänge oder auch des Inhaltstyps. Dadurch können Sie herausfinden, ob der Server Ihnen eine riesige Datenmenge als Antwort auf Ihre Anfrage schickt oder ob der Server versucht, Ihnen Binärdaten anstelle von HTML, Text oder XML (die allesamt leichter in JavaScript zu verarbeiten sind als Binärdaten) zu schicken.

In diesen Fällen nutzen Sie den entsprechenden Namen der Kopfdaten und übergeben ihn an die getResponseHeader()-Methode des XMLHttpRequest-Objekts. Um also die Länge einer Antwort zu bekommen, verwenden Sie request.getResponseHeader("Content-Length");. Um den Inhaltstyp herauszufinden, fragen Sie den Wert request.getResponseHeader("Content-Type"); ab.

In vielen Anwendungen erreichen Sie mit HEAD-Anfragen keine zusätzliche Funktionalität, sondern verlangsamen Ihre Anfrage womöglich noch (da Sie erst eine HEAD-Anfrage stellen, um Daten über die Antwort zu bekommen, und danach dann eine GET- oder POST-Anfrage, um die Antwort tatsächlich zu erhalten). Für den Fall, dass Sie jedoch unsicher bezüglich des Skripts oder der serverseitigen Komponente sind, ermöglicht Ihnen eine HEAD-Anfrage, die grundlegenden Daten zu bekommen, ohne die Antwortdaten zu verarbeiten oder die Bandbreite für die Antwort in Anspruch zu nehmen.

Resümee

Für viele Ajax- und Web-Programmierer wird der Stoff in diesem Artikel ziemlich fortgeschritten sein. Wozu soll es gut sein, eine HEAD-Anfrage zu stellen? Wann müssen Sie wirklich einmal den Statuscode der Weiterleitung in Ihrem JavaScript explizit behandeln? Das sind gute Fragen; für einfache Anwendungen lautet die Antwort, dass diese fortgeschrittenen Techniken wahrscheinlich nicht vonnöten sind.

Das Internet ist jedoch nicht mehr ein Ort, an dem einfache Anwendungen toleriert werden; die Benutzer sind erfahrener geworden, Kunden erwarten Robustheit und fortgeschrittene Fehlerausgaben, Manager werden gefeuert, weil die Anwendung ein Prozent der Zeit nicht erreichbar ist.

Also ist es Ihr Job, mehr als nur eine einfache Anwendung zu schreiben -- und dafür benötigen Sie ein tiefgehendes Verständnis des XMLHttpRequest-Objekts.

  • Wenn Sie die verschiedenen Verbindungsstatus berücksichtigen -- und verstehen, wie diese von Browser zu Browser variieren -- können Sie eine Anwendung schnell debuggen. Sie könnten sich sogar eine geniale Funktionalität einfallen lassen, um den Status der Anfrage an Ihre Benutzer und Kunden zurückzumelden.
  • Wenn Sie die Statuscodes durchschaut haben, können Sie in Ihrer Anwendung mit Skriptfehlern, unerwarteten Antworten und Grenzfällen umgehen. Das sorgt dafür, dass Ihre Anwendung immer funktioniert und nicht nur dann, wenn alles haargenau nach Plan läuft.
  • Wenn Sie darüber hinaus noch wissen, wie HEAD-Anfragen gemacht, die Existenz einer URL überprüft oder der Zeitpunkt der letzten Änderung einer Datei abgefragt wird, dann können Sie sicher stellen, dass Ihre Benutzer perfekte Seiten mit aktuellen Informationen zu sehen bekommen und (das ist das Wichtigste) sie damit überraschen, wie robust und vielseitig Ihre Anwendung ist.

Dieser Artikel sorgt nicht dafür, dass Ihre Anwendungen schrill werden oder Sie Text mit einem gelben Farbverlauf hervorheben können oder den Eindruck einer Desktop-Anwendung bekommen. Dies sind zwar alles Stärken von Ajax (und Themen, die in kommenden Artikeln behandelt werden), doch das wäre in gewissermaßen nur das Tüpfelchen auf dem "i". Wenn Sie Ajax verwenden können, um die solide Grundlage zu schaffen, damit Ihre Anwendung Fehler und Probleme gut abfängt, dann werden die Benutzer auf Ihre Seite und zu Ihrer Applikation zurückkehren. Wenn Sie dem Ganzen dann noch das gewisse Etwas in Form von visuellen Finessen hinzufügen, über die ich in den folgenden Artikeln sprechen werde, dann haben Sie begeisterte und glückliche Kunden. (Ehrlich, den nächsten Artikel werden Sie wirklich nicht verpassen wollen!)

Ressourcen

Weiterführende Literatur

Lesen Sie weiter: Teil 4 - Mit dem DOM die Web Response optimal verwerten>

<< Zurück zu Teil 2 - Wie Sie mit JavaScript und Ajax asynchron anfragen


Themen

Buchreihen

Special Interest

International Sites

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