Wie man das implementiert, dazu gibt es viele Tutorials im Netz. Allerdings beziehen sich die meisten auf Java. Wie ist das aber mit anderen Programmiersprachen? Wenn man nicht einen eigenen Server im Netz betreibt, wir es schwierig mit Java. Einfache Hostingangebote für ein paar Euro im Monat bieten keinen Javaserver an. Die Verwendung von Skriptsprachen wie Perl, PHP oder Python ist jedoch fast immer möglich.
Deshalb möchte ich hier zeigen, wie man REST und Perl miteinander verheiratet. Wir werden sehen: Es ist unglaublich einfach! Besonders intereressant ist es, dieses Verfahren mit einer einfachen Java-Implementierung des gleichen Problems zu vergleichen. Dazu plane ich noch zwei weitere Artikel.
REST mit dem CGI Module
Unser kleiner Restservice soll dem Benutzer einen Buchkatalog zur Verfügung stellen. Die Implementierung selbst soll zunächst absolut simpel mit dem CGI Modul erfolgen. Eine zweite soll sich auf Perl-Dancer stützen. Mit diesem Perl Web-Framework kann man besonders schnell und einfach eine Restschnittstelle aufbauen. Wir werden sehen Perl mit REST ist wie ein Fisch im Wasser.Zur Persistierung der Daten soll das DB_File Modul genutzt werden. Damit kann man Hashes fast vollkommen transparent im Filesystem halten, d.h. man muß sich um fast nichts kümmern. Somit ist hier auch kein Zugriff auf eine Datenbank notwendig, der das Verfahren verkomplizieren würde.
Mit folgendem Code kann man GET und POST für unseren bookdb-REST Service ausführen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #!/usr/bin/perl use strict; use warnings; use CGI; use DB_File; use JSON; my $page = new CGI; my $rest_data = $page->header('application/json'); # ------ Initialisierung des Datenzugriffs über DB_File ------ $file = "/local/books.data"; my %data; my $db = tie %data, "DB_File", $file or die "err open $!\n"; # ------ Prüfen ob über den definierten Pfad auf den REST Service zugegriffen wurde ------ my $path_info = $ENV{ 'PATH_INFO' }; $path_info =~ /^\/(.+)?\/(.*)$/; my $type = $1; my $id = $2; if( not $type eq 'bookdb' ){ exit; } # ------ Zugriffsmethode ermitteln my $request_method = $ENV{ 'REQUEST_METHOD' }; if( $request_method eq 'GET' ){ # ------ Daten holen # ??? keine Daten zur ID in DB_File gefunden ??? # ja: der Restservice liefer den Fehler 404 if( not exists $data{ $id } ){ $rest_data = $page->header('text/html', '404 Not found'); } # nein: Daten sind da und werden aus DB_File geholt else{ my %book = ( id => $id ); # Daten sind in DB_File im Semikolon separierten Format gespeicht # und werden hier in einen Hash umgewandelt ( $book{ title }, $book{ author } ) = split /;/, $data{ $id }; $rest_data .= to_json( \%book ); } } elsif ( $request_method eq 'POST' ) { # ----- Daten schreiben # Json Daten aus dem POST Request holen und auf einen Hash abbilden my $book = from_json( $page->param( 'POSTDATA' ) ); # Daten in den DB_File Hash schreiben $data{ $book->{id} } = "$book->{title};$book->{author}"; $rest_data = $page->header('text/html') . "OK\n"; } print $rest_data; # http Response ausgeben |
Test des REST Service mit curl
Die http-Requests um den REST Service aufzurufen, kann man mit curl ausführen.POST - man legt das Buch 'REST und Perl' an:
$ curl -i -H 'Content-Type: application/json' -X POST -d '{ "id":"5555", "title" : "REST und Perl", "author" : "A. Parker" }' http://localhost/cgi-bin/rest_cgi.pl/bookdb/ HTTP/1.1 200 OK Date: Wed, 19 Nov 2014 08:29:26 GMT Server: Apache/2.4.10 (Fedora) mod_fcgid/2.3.9 PHP/5.5.18 mod_perl/2.0.9-dev Perl/v5.18.4 Transfer-Encoding: chunked Content-Type: text/html; charset=ISO-8859-1
GET - man holt sich die Daten mit der ID:
$ curl -i http://localhost/cgi-bin/rest_cgi.pl/bookdb/5555 HTTP/1.1 200 OK Date: Wed, 19 Nov 2014 08:31:22 GMT Server: Apache/2.4.10 (Fedora) mod_fcgid/2.3.9 PHP/5.5.18 mod_perl/2.0.9-dev Perl/v5.18.4 Transfer-Encoding: chunked Content-Type: application/json; charset=ISO-8859-1 { "id" : "5555", "title" : "REST und Perl", "author" : "A. Parker" }
Wer lieber eine graphische Benutzeroberfläche mag, kann natürlich auch mit Browser Plugins arbeiten, wie z.B. mit dem "Advanced REST Client" von Chrome. Aber letztlich ist das auch schon wieder deutlich umständlicher als curl.
REST mit Dancer
Dancer ist ein leicht verständliches WEB Framework für Perl. Es ist unlängst die Version 2 herausgekommen. Es gibt sogar einen Artikel über REST und Dancer. Leider ist dieser nur fragmentarisch. So wird dort nicht auf POST eingegangen, was für ein vollständiges Beispiel elementar ist. Ich habe nun das obige CGI Beispiel vollständig in Dancer implementiert. Natürlich verwende ich wieder DB_File für die Persistierung.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/usr/bin/perl use JSON qw( decode_json ); use Dancer; use DB_File; use strict; # init DB_File Hash my %data; my $file = "/local/books_dancer.txt"; my $db = tie %data, "DB_File", $file or die "err open $!\n"; # Daten sollen im JSON Format ausgegeben werden set serializer => 'JSON'; get '/bookdb/:id' => sub { # ------ Daten holen # ??? Daten im DB_File Hash gefunden ??? # nein: 404 Status zurückgeben if( not exists $data{ params->{id} } ){ status 'not_found'; return "Book does not exist, unable to get"; } else{ # ja: Daten auslesen und zurückgeben my %book = ( id => params->{id} ); ( $book{ title }, $book{ author } ) = split /;/, $data{ params->{id} }; return { %book }; } }; post '/bookdb/' => sub { # ----- Daten schreiben my $json = request->body; my $params = decode_json $json; $data{ $params->{id} } = "$params->{title};$params->{author}"; }; dance; |
Eine schöne REST URL
Natürlich will man in der Regel das CGI-Skript nicht direkt in der REST URL aufrufen. Aber eine schöne URL ist releativ einfach in der Apache Konfiguration umsetzbar. Im nachfolgenden Beispiel ist das Problem für das Dancer Beispiel gelöst. Mit der CGI.pm Variante kann man das analog machen.httpd Conifg:
1 2 3 4 5 6 7 8 9 10 | <Directory "/var/www/cgi-bin/simple_rest/public"> AllowOverride None #Options None Require all granted AddHandler cgi-script .cgi .pl Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch #Options FollowSymLinks +ExecCGI </Directory> ScriptAlias /test-rest/ /var/www/cgi-bin/simple_rest/public/dispatch.cgi/ |
Einbindung in JQuery
Der Vollständigkeit halber sei hier noch gezeigt, wie man den REST Service via JQuery in seine WEB-Site einbindet. Es fehlt die POST Implementierung, weil der Fokus hier auf den REST Service liegt. Die Umsetzung ist aber analog zu der von GET.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Test rest + json + jquery</title> <script src="jquery/jquery.js" type="text/javascript"></script> </head> <body> <script type="text/javascript"> function getBookdata () { jQuery.ajax({ type: "GET", url: "http://localhost/test-rest/bookdb/5556", contentType: "application/json; charset=utf-8", dataType: "json", success: function (data, status, jqXHR) { alert("Rest-Service geholt!\ndata.id:" + data.id + "\ndata.author:" + data.author + "\ndata.title:" + data.title); $("#bookid").html( data.id ); $("#bookauthor").text( data.author ); $("#booktitle").html( data.title ); }, error: function (jqXHR, status, errorThrown) { alert("Rest-Service Fehler! status:" + status + " errorThrown:" + errorThrown); } }); } $(document).ready(function(){ debugger; getBookdata(); }); </script> Aktuelles Buch: <br><br> ID: <div id="bookid"></div> <br> Autor: <div id="bookauthor"></div> <br> Titel: <div id="booktitle"></div> </body> </html> |
CGI vs. Dancer
Welche Implementierung sollte man benutzen?Zunächst fällt auf, daß beide Implementierungen schlank sind. Dancer kommt mit noch etwas weniger Code aus.
Die CGI Variante verbirgt nichts. Man sieht genau, was man macht, um den REST Service zu implementieren. Im Fehlerfall kann das ein unschätzbarer Vorteil sein. Des weiteren ist man nicht von externen Modulen abhängig, sondern kommt mit Perl-Core aus. Das ändert sich natürlich in dem Moment, wenn man relationalen Datenbanken arbeitet und DBI einsetzt. Wer allerdings mal Dancer installiert hat, weiß das hier nicht nur einige zusätzliche Module gebraucht werden, sondern wie so oft bei Frameworks, das halbe CPAN nachgereicht wird. Eine wirklich unschöne Sache. Beim billig WEB-Hosting Account wird man hier scheitern, weil sich der Provider dabei querstellt. Deshalb hat die CGI-Variante definitiv ihre Berechtigung. Interessanterweise habe ich sie so nicht im Netz gefunden. Alle Autoren, die sich um REST und Perl kümmern, greifen gleich zu irgendeinem Framwork, was, wie wir oben gesehen haben, aber nicht notwendig ist. Die CGI.pm Lösung ist voll funktional und trotzdem simpel.
Aber warum sollte man dann überhaupt zu Dancer greifen? Der einfachste Grund: Man hat einen eigenen Server und setzt sowieso schon Dancer ein. Natürlich ist die Flexibilität hier auch höher. Im Dancercode ist es ein minimaler Aufwand, die Ausgabe von JSON auf XML umzustellen. CGI.pm hätte da ein Problem, aber ein lösbares. Allerdings mit deutlich höheren Kosten. Aber wer braucht schon unbedingt eine XML Response oder sogar eine flexible Umschaltung zwischen JSON und XML.
Auch sieht der Code mit Dancer einfacher und schöner aus. Im Fehlerfall sollte man sich aber gut mit dem HTTP-Protokoll und Dancer auskennen, sonst könnte der Frust deutlich schneller und länger in den roten Bereich ausschlagen als bei der CGI.pm Variante. Auch Pfade für die REST URL sind deutlich schöner umzusetzten.
Kurzum der Profi mit dem eigenen Server wird ehr zu Dancer greifen und der Amateur mit dem billig Hosting Account ehr zu CGI.pm. Ich war jedenfalls erstaunt, wie weit man mit CGI.pm kommt und würde die Variante bevorzugen, wenn ich nicht sowieso Dancer einsetze.
Aber egal welchen Weg man geht: Man kann ganz klar sagen: Perl + REST Service rockt!!! Das gilt auch für den Hobbyprogrammierer, weil der Einarbeitungsaufwand gering ist. (Wenn man diesen Artikel gelesen hat ;-)
Keine Kommentare:
Kommentar veröffentlichen