Ajax-Funktionen mit JSON und Perl

JSON als Alternative zu Nachrichtenübertragung auf XML-Basis

Eine interessante Alternative zu AJAX-Funktionen auf XML-Message-Basis bietet die JSON-Technologie an. Die Übertragung der Nachrichten vom Browser zum Server erfolgt dabei auf Basis einer einfachen Datenstruktur, die am besten beschrieben werden kann als Verschachtelung von Arrays und Hashes. In den meisten Programmiersprachen werden diese Strukuren nativ unterstützt, was die Verbreitung der Technologie extrem vereinfacht.

Der Overhead des durch die XML-Strukturen anfallenden Codes entfällt, somit wird die Übertragung und das Verarbeiten der Nachrichten vereinfacht. Weder auf der Seite des Clients noch auf der des Severs muss eine Transformation der Daten über XSLT oder aber ein Zusammensuchen der Daten durch aufwändiges Navigieren durch das DOM erfolgen. Zudem muss sich der Programmierer keine Gedanken um das Finden einer geeigneten XML-Struktur machen um die Daten hin- und her zu transportieren.

Eine Besonderheit ist dabei, daß die Daten auf der Client-Seite direkt als gültiger Javascript-Code vorliegen. Sie können direkt mit Javascript weiterverarbeitet werden.

JSON Nachrichtenstruktur

Die Strutur der zu übetragenden Nachrichten sind ausführlich auf www.json.org beschrieben.

Beispiel für eine JSON-Nachricht:

{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": [{
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef":
"A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML", "markup"]
}]
}
}
}

Implementieren auf Client-Seite

Um die AJAX-Funktionalität auf der Client-Seite zu implementieren, benötigt man eine Javascript-Library. Eine solche ist auch über die oben genannte Seite www.json.org zu beziehen, allerdings nicht besonders leicht zu finden.

Deshalb hier die direkten Links:

Weiterhin ist eine Library HTTP.Request hilfreich, mit deren Hilfe die HTTP-Übertragung selbst Browsertransparent durchgeführt werden kann. Sie kann hier bezogen werden. Allerdings ist zu beachten, daß in der aktuellen Version dieser Library zur Übertragung immer automatisch der Content-Type application/x-www-form-urlencoded verwendet wird. Dies führt z.B. bei einem Perl-Backend auf dem Server dazu, daß der Request nicht mehr korrekt ankommt sondern nur zerteilt. Zum Beheben dieses Problems sollte die folgende Zeile in Request.js geändert werden von:


this.transport.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );

in

this.transport.setRequestHeader( "Content-type", "plain/text" );

Mit diesen Vorraussetzungen kann nun der Client implementiert werden:

<script type="text/javascript" src="/global/lib/json.js"></script>
<script type="text/javascript" src="/global/lib/HTTP/Request.js"></script>
<script type="text/javascript">
var req, id = 0, since_msg = new Date();
// vor 5 Stunden
since_msg.setTime(new Date().getTime() - 1000 * 60 * 60 * 5);

function aktualisieren () {
var txt = document.getElementById('eingabe');

req = new HTTP.Request({
uri: '/test/testajax',
postbody: JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}),
onSuccess: function (trans) {
var data;
try {
var data = eval('('+trans.responseText+')'); // JSON "parsen"
} catch(e) {
alert('eval: Ungültiges JSON: ' + e);
return;
}
// hier kann ggf. noch auf data.error geprüft werden
nachrichten(data.result);
}
});
/*txt.value = ''; // Text im Eingabefeld nach Versand löschen*/
txt.focus(); // Benutzer kann weiter Text eingeben
}

function nachrichten (result) {
var ausgabe = document.getElementById('ausgabe'); // DIV fuer Text
if (result) { // Antwort auf update_box
ausgabe.innerHTML="";

for (var i = 0; i < result.length; i++) {
ausgabe.innerHTML += result[i] + "<br>\n";
}
}
}

</script>

<form action="#" onsubmit="aktualisieren();">
Ihr Text: <input type="text" id="eingabe" value="" onkeyup="aktualisieren();"/>
<input type="button" value="senden + empfangen"
onclick="aktualisieren();"/>
</form>
<div id="ausgabe"></div>

Die wichtigsten Code-Stellen:


JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}

Hier wird ein Hash der die Nachrichtenbestandteile enthält mittels JSON.stringify zu einem JSON-String umgewandelt. Die RPC-Methode, die dabei auf dem Server aufgerufen wird, nennt sich finduser. Sie bekommt die Parameter txt.value, since_msg.getTime(). id ist eine fortlaufende Nummer, die zur Identifizierung des Requests bei asynchron eintreffenden Ergebnissen dient.

Der erzeugte JSON-String zur Übertragung sieht dann so aus:

{"method":"finduser","id":0,"params":["micha",1140627355276]}

Mittels der Anweisung:

req = new HTTP.Request({
uri: '/test/testajax',
postbody: JSON.stringify( {
method : 'finduser',
id : id++,
params : [ txt.value, since_msg.getTime() ]
}),
onSuccess: function (trans) {
var data;
try {
var data = eval('('+trans.responseText+')'); // JSON "parsen"
} catch(e) {
alert('eval: Ungültiges JSON: ' + e);
return;
}
// hier kann ggf. noch auf data.error geprüft werden
nachrichten(data.result);
}
});

wird der HTTP-Request übetragen. onSuccess beinhaltet eine Funktion, die bei einem erfolgreichen Request ausgeführt wird. Hier wird die Antwort verarbeitet und dynamisch in die dargestellte Seite eingebaut.

Implementierung auf der Server-Seite

Auf der Server-Seite ist die Implementierung natürlich abhängig von der Programmiersprache der Wahl. JSON-Implementierungen sind in zahlreichen gängigen Programmiersprachen vorhanden, Links dazu sind auch über www.json.org zu finden.

Hier soll die Implementierung in Perl betrachtet werden. Wie man es von Perl gewohnt ist, steht ein fertiges CPAN-Modul JSON zur Verfügung, daß das Einbinden denkbar einfach macht. Es funktioniert (unter anderem) mit einer RPC-Schnittstelle á la SOAP. Im Idealfall muss man sich noch nicht einmal mit den JSON-Datenstrukturen beschäftigen, sondern bekommt die Parameter direkt als Funktionsparameter geliefert und gibt Perl-Datenstrukturen zurück. Die nötigen Umwandlungen passieren im Hintergrund.


package MyJSON;
use strict;
sub finduser {
my $server = shift;
my $username = shift;
my $time = shift;
#...
# return a scalar value or a hashref or an arryaref. Z.B.:
# return {'username'=>"Franz Mustermann", 'userid'=>14};
}

package main;
use strict;
use JSONRPC::Transport::HTTP;
use MyJSON;

# a la XMLRPC::Lite
JSONRPC::Transport::HTTP::CGI->dispatch_to('MyJSON')->handle();

  • Both comments and trackbacks are currently closed.
  • Trackback URI: http://www.frankl.info/wordpress/know-how/perl/ajax-funktionen-mit-json-und-perl/trackback/
  • Comments RSS 2.0

3 Responses to “Ajax-Funktionen mit JSON und Perl”

  1. Stefan Says:

    Hi,
    erstmal danke für die Zusammenfassung zu dem Thema (auch wenn sie inhaltlich stark an einen iX-Artikel erinnert…).
    Trotzdem noch eine Frage: Warum sollte nicht der Content-Type “application/x-www-form-urlencoded” benutzt werden? Meines Wissens ist dies der Standard auch für alle normalen Formulare etc., inwiefern soll ein Perl-Backend damit nicht klar kommen?

    Danke für die Aufklärung und besten Gruß
    Stefan.

  2. Administrator Says:

    Hallo Stefan,

    ja, die Grundlage dafür war auch ein iX-Artikel (und ja, einen Verweis darauf werde ich noch einfügen :-) ). Allerdings habe ich mich beim Lesen des Artikels sehr geärgert, dass viele wichtige Hinweise gefehlt haben, wo genau man z.B. die js-Libraries bezieht und so weiter. Da ich sehr viel Zeit investiert habe, das Ganze zum Laufen zu bringen, habe ich gedacht, es ist sinnvoll das weiterzugeben.

    Zu der Sache mit dem Content-Type: Klar wird application/x-www-form-urlencoded für normale Formulare usw. verwendet. Jedoch hat die Kommunikation mit JSON nichts mit Formularen zu tun. Konkrete Probleme gab es bei der Verwendung der JSON-Perl Module (die waren mit Perl-Backend gemeint). Ich habe die nur mit dem genannten Header in Gang gebracht. Vielleicht gibt es mittlerweile neue Versionen der Perl-Module oder der JS-Libs. Das müsste mal geprüft werden. Wenn Du hier andere/neue Erkenntnisse hast, sag bitte Bescheid.

    Beste Grüße,
    Michael Frankl

  3. Stefan Says:

    Hallo,
    das Verhalten mit dem Content-Type ist wirklich komisch - ich hatte gestern nämlich auch ein Problem damit, habe es heute mit text/plain probiert und siehe da, es funktioniert :-)
    Das Problem konnte ich soweit einschränken, dass es bei mir immer bei Leerzeichen innerhalb der an die RPC-Methode zu übergebenden Daten auftrat - ohne Leerzeichen funktioniert es aber. Was mich dabei wundert: Warum klappt es (angeblich) in dem iX-Artikel, schließlich ist die dort realisierte Shoutbox wohl kaum ohne Leerzeichen sinnvoll…

    Egal, trotzdem danke für den Hinweis (sonst hätte ich vermutlich ewig weiter gesucht).
    Gruß
    Stefan.

    PS: Der JavaScript-JSON-Parser unter www.json.org/json.js scheint sich geändert zu haben, oder sehe ich das falsch? Meine (alte) Version hat ca. 5k und enthält noch die JSON.stringify-Funktion, die aktuelle (ca. 3k) hingegen nicht mehr. Kannst Du das bestätigen? Komisch, komisch, …