Web Engineering 1/AJAX und JSON

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>Javascript</language>
__NOTOC__
{{Ziele|
* Allgemeines Verständnis der Request-Response-Kommunikation
* AJAX: Konzept des asynchronen Datenaustauschen
* JSON: Alternatives Format zur Repäsentation von Objekten
* XHR: Umsetzung von AJAX in Javascript
* jQuery bietet einen komfortablen Aufsatz
|
* Senden von Anfragen und Akzeptieren von Antworten (am Client)
* Emfang von Anfragen und Versand von Antworten (am Server)
* Intuitiver Umgang mit GET und POST
* Objektserialisierung mit JSON und XML
|
* Node.js Server zur Verwaltung der Nutzerdaten
** Anmeldung und Registrierung von Nutzern mit Passwort
** Serverseitige Speicherung der Nutzerdaten
** Bei erfolgreicher Lösung des Sudoku, Speicherung der benötigten Zeit
* Sudoku-App zeigt nach Anmeldung die besten 10 eigenen Spielergebnisse
}}

Wie bereits im Kapitel [http://www.programmingwiki.de/Web_Engineering_1/HTTP HTTP] erwähnt handelt es sich bei HTTP um ein Request-Response-Protokoll. Jede einzelne Kommunikation ist abgeschlossen und besteht immer aus dem Aufbauen der Verbindung, dem Senden der Anfragedaten, dem Empfang der Ergebnisdaten und dem Schließen der Verbindung. Dieser Prozess benötigt Zeit die zum Großteil für die Übertragung und serverseitige Operationen aufgewendet wird. Eine Client-Anwendung bleibt an dieser Stelle nichts anderes übrig als zu warten.

Im Kapitel [http://www.programmingwiki.de/Web_Engineering_1/Javascript_2 Javascript 2] wurde bereits das Problem von wartenden Prozessen erörtert und festgestellt, dass zu lange Wartezeiten den Browser blockieren. Mit der asynchronen Programmierung wurde aber auch eine Technik vorgestellt, die diesem Problem entgegentritt. AJAX (Asynchronous Javascript And Xml) nutzt genau diese Technik indem Callback-Funktionen zur Verarbeitung des Anfrageergebnisses asynchron ausgeführt werden.

==AJAX mit Javascript==
Javascript stellt für die HTTP-Kommunikation das <tt>XMLHttpRequest</tt> Objekt bereit. Mit der Funktion <tt>open</tt> wird die Verbindung initiiert und <tt>send</tt> löst das Senden des Request aus. Beide Funktionen akzeptieren diverse Parameter zur Steuerung der Kommunikation. Für die Reaktion auf Serverantworten stehen entsprechende Callback-Funktionen bereit.
{|
|style="width: 22em;vertical-align:top;"| 
<html><iframe width="420" height="300" src="//jsfiddle.net/pw_we1/33rw9j5n/embedded/js,html,result/" allowfullscreen="allowfullscreen" frameborder="0"></iframe></html>
|style="vertical-align:top;padding-left:1em;"|
* ''Analysieren Sie den Beispielaufruf. Machen Sie sich klar, wann der eigentliche Aufruf der Callback-Funktion erfolgt.''
* ''Untersuchen Sie den <tt>true</tt> Parameter an der Funktion <tt>open</tt> und erklären Sie seine Wirkung''
* ''Fügen Sie eine Fehlerbehandlung hinzu und testen Sie die verschiedenen Statuscodes''
* ''Probieren Sie auch Aufrufe mit Parametern (siehe [http://fixer.io/ fixer.io])''
* ''Übernehmen Sie das Beispiel in eine lokale Datei und platzieren Sie diese auf Ihrem Webserver''
* ''Legen Sie eine PHP Datei daneben und sprechen Sie diese über den <tt>XMLHttpRequest</tt> an.''
* ''Versuchen Sie auch POST Anfragen und versenden Sie verschiedene Arten von Parametern''
|}

==AJAX mit jQuery==
Mit jQuery werden Funktionen bereitgestellt, die das Javascript <tt>XMLHttpRequest</tt> Objekt kapseln und die gleiche Funktionalität bieten. Jedoch wird hier eine andere Syntax für den Aufruf verwendet, so dass dieser als eine einzige Funktion definiert werden kann. Sämtliche Aufrufparameter, inklusive der Callback-Funktionen, werden hier als Eigenschaften eines Objekts an die Funktion übergeben. Die möglichen Eigenschaftsnamen (Parameter) können in der [http://api.jquery.com/jquery.ajax/ jQuery-Dokumentation] nachgelesen werden.
{|
|style="width: 22em;vertical-align:top;"| 
<html><iframe width="400" height="290" src="//jsfiddle.net/pw_we1/p1qpb9c9/embedded/js,html,result/" allowfullscreen="allowfullscreen" frameborder="0"></iframe></html>
|style="vertical-align:top;padding-left:1em;"|
* ''Fügen Sie eine Fehlerbehandlung hinzu und testen Sie die verschiedenen Statuscodes''
* ''Probieren Sie auch Aufrufe mit Parametern (siehe [http://fixer.io/ fixer.io])''
* ''Recherchieren Sie die jQuery Funktionen <tt>get</tt> und <tt>post</tt> und machen Sie Unterschiede und Gemeinsamkeiten mit <tt>ajax</tt> aus''
* ''Übernehmen Sie das Beispiel in ein lokale Datei und kommunizieren Sie mit dem oben erstellten PHP Skript''
* ''Installieren Sie [https://nodejs.org/ Node.js] implementieren Sie einen [https://nodejs.org/api/synopsis.html einfachen Webservice] der die Aufgaben des PHP Skripts übernimmt''
* ''Implementieren Sie im Node.js Server die Behandlung von POST Anfragen mit entsprechenden Parametern''
|}

==Serialisierung==
Um auch Objekte, die sich ausschließlich im Arbeitsspeicher befinden, übertragen zu können, müssen diese serialisiert (in eine sequentiell übertragbare Struktur gebracht) werden. Für Javascript Objekte ist die Javascript Object Notation (JSON) eine solche Struktur. Mit <tt>JSON.stringify</tt> kann ein Objekt in eine JSON-Zeichenkette überführt werden. Diese kann nun, wie jede andere Zeichenkette, übertragen werden. Die Erzeugung eines Javascript Objekts aus der JSON-Zeichenkette erfolgt mit <tt>JSON.parse</tt>. JSON ähnelt der [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer Literaldefinition] von Objekten in Javascript (eingefasst in geschweifte Klammern werden die Eigenschaften des Objekts durch Komma getrennt als Name-Wert-Paare aufgelistet). JSON ist aber sehr viel strikter. Neben Notationsunterschieden sind z.B. rekursive Objektreferenzen nicht serialisierbar.
{|
|style="width: 22em;vertical-align:top;"| 
<html><iframe width="400" height="380" src="//jsfiddle.net/pw_we1/4zpemo21/embedded/js,html,result/" allowfullscreen="allowfullscreen" frameborder="0"></iframe></html>
|style="vertical-align:top;padding-left:1em;"|
* ''Experimentieren Sie mit dem Beispiel. Testen Sie die Grenzen der JSON-Serialisierung aus. Probieren Sie auch mit leeren Texten und identischen Namen''
* ''Versuchen Sie ein Objekt einer selbst definierten "Klasse" zu serialisieren. Welches Problem entsteht hier und wie lässt es sich lösen?''
* ''Recherchieren Sie Möglichkeiten zur XML Serialisierung von Javascript Objekten. Testen Sie verschiedene Bibliotheken.''
* ''Vergleichen Sie XML und JSON bezüglich ihres Nutzens zur Serialisierung von Objekten. Welche Vor- und Nachteile gibt es?''
* ''Erweitern Sie das AJAX Beispiel und nutzen Sie die Möglichkeiten der Serialisierung zur Übertragung von Objekten''
|}

==Validierung==

<html>
<script src="https://syssgx.github.io/xml.js/js/xmllint.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/codemirror.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/codemirror.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.23.0/mode/xml/xml.js"></script>
</html>
<html>
<style>
  .CodeMirror {
    border: 1px solid silver;
    border-width: 1px;
    line-height: 1.2em;
    height: 400px;
  }
</style>
<script>
$(document).ready(function() {
  var options = {
    tabSize: 2,
    indentWithTabs: true,
    lineNumbers: true,
    lineWrapping: true
  };
  CodeMirror.fromTextArea(document.getElementById("xsd"), options);
  CodeMirror.fromTextArea(document.getElementById("xml"), options);
});
</script>
</html>
{| style="100%;"
| colspan="2" | 
|-
! style="width: 50%;"|Schema (XSD)
! style="width: 50%;"|Inhalt (XML)
|-
|<html><code id="xsd">
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="shiporder">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="orderperson" type="xs:string"/>
      <xs:element name="shipto">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="address" type="xs:string"/>
            <xs:element name="city" type="xs:string"/>
            <xs:element name="country" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="item" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="title" type="xs:string"/>
            <xs:element name="note" type="xs:string" minOccurs="0"/>
            <xs:element name="quantity" type="xs:positiveInteger"/>
            <xs:element name="price" type="xs:decimal"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="orderid" type="xs:string" use="required"/>
  </xs:complexType>
</xs:element>
</xs:schema>
</code></html>
|<html><textarea id="xml">
<?xml version="1.0" encoding="UTF-8"?>
<shiporder orderid="889923" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="shiporder.xsd">
  <orderperson>John Smith</orderperson>
  <shipto>
    <name>Ola Nordmann</name>
    <address>Langgt 23</address>
    <city>4000 Stavanger</city>
    <country>Norway</country>
  </shipto>
  <item>
    <title>Empire Burlesque</title>
    <note>Special Edition</note>
    <quantity>1</quantity>
    <price>10.90</price>
  </item>
  <item>
    <title>Hide your heart</title>
    <quantity>1</quantity>
    <price>9.90</price>
  </item>
</shiporder> 
</textarea ></html>
|-
|colspan="2" | 
<run id="589b7a76bf85c">
var res = validateXML({
  xml: $("#xml").val(),
  schema: $("#xsd").val(),
  arguments: ["--noout", "--schema", "XSD", "XML"]
});
console.log(res.toString());
</run>
|}

==Projekt und weitere Übungen==
{{Projekt|
Stellen Sie die Anmeldung auf Node.js um. Speichern Sie zusätzlich zu jedem Nutzer seine Ergebnisse (benötigte Zeit zur Lösung eines Sudokus) und geben Sie diese in der Webapplikation aus.
Beispiellösung <document>WE1-P8.zip</document>}}
# Analysieren Sie das folgende Dokument mit Firebug. <code id="5888b46a0dafc" readonly>
<!DOCTYPE html>
<html>
<head>
  <title>AJAX: readyState</title>
  <script type="text/javascript">
    // Objekt erzeugen (ohne Polyfill)
    var xhr = new XMLHttpRequest();
    function handleStateChange() { // aktuellen Status melden
      alert("xmlHttpObject.readyState = " + xhr.readyState + (xhr.readyState >= 3 ? " HTTP-Status = " + xhr.status : ''));
    }
    // Mit dem Laden des Dokuments ausführen
    window.onload = function() {
      // Handler hinterlegen
      xhr.onreadystatechange = handleStateChange;
      xhr.open('GET', 'bsp02.html');
      // Anfrage abschicken
      xhr.send(null);
    }
  </script>
</head>
<body>
  <p>AJAX-readyState-Codes und HTTP-Status</p>
</body>
</html>
</code>
# Arbeiten Sie den [http://developerblog.redhat.com/2014/05/20/communicating-large-objects-with-web-workers-in-javascript/ online-Beitrag] über die Verarbeitung großer Datenmengen (im JSON-Format) durch und implementieren Sie die drei genannten Strategien. Bewerten Sie das jeweilige Ergebnis.
# Verwenden Sie einen sniffer, z.B. [http://www.telerik.com/fiddler Fiddler] (kostenlos), um die Datenkommunikation zwischen Client und Server zu beobachten und zu interpretieren. Als HTTP-Debugging-Proxy platziert sich Fiddler zwischen dem Client und dem (entfernten) Server, der die Anfrage entgegennimmt. Da ist es ein Leichtes, ein- und ausgehenden HTTP-Verkehr zu protokollieren - und zu ändern.
# Verwenden Sie den [https://maps.googleapis.com/maps/api/geocode/json?address=02826+G%C3%B6rlitz+Br%C3%BCckenstra%C3%9Fe+1 Geoservice] von Google und stellen Sie damit interessante Daten tabellarisch zusammen.
# Erschließen Sie die (komfortablen) Methoden der AJAX-Verarbeitung von jQuery und kommentieren Sie ein selbsterstelltes Beispiel mit JSON-Daten.
# Bei Interesse: Verwenden Sie AtoCC zur Herstellung eines JSON-Parsers.