![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||||||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Perl SnapshotAus der CGI-Trickkiste IIvon Michael Schilli |
![]() |
Abbildung 1 zeigt, was mit reinem HTML heutzutage alles an Tipp- und Klick-Schnickschnack möglich ist: Popup-Menüs, die auf Knopfdruck hervorschnellen und eine Auswahl zulassen, drückbare Radio- und Checkbuttons, ein- oder mehrzeilige Textfelder, scrollbare Listen und schließlich Knöpfe zum Senden bzw. Zurücksetzen der Formularinformation.
Das CGI-Skript aus Listing form.pl zeichnet für diese Ausgabe verantwortlich. Die Tags :standard und :html3 exportieren aus CGI.pm reguläre HTML- und Tabellen-Funktionen.
Zeile 7 legt den HTML-Code für ein Popup-Menü in der Variablen $popup_menu ab. Das Teil trägt den Namen farbe1. Und genauso wird auch die Variable heißen, die der Browser nach dem Absenden des Formulars zurück an den Server sendet, gesetzt auf den vom Benutzer gewählten Wert. 'r', 'g' und 'b' stehen intern zur Auswahl, der Benutzer freilich bekommt nur die über den Hash %labels gemappten Wörter Rot, Grün und Blau zu sehen. 'r', also 'Rot' selektiert der Browser vor.
Eine radio_group wie die in Zeile 13 besteht aus einer Gruppe von Radio-Buttons, von denen genau einer selektiert ist und so den Wert der Ausgangsvariablen bestimmt.
Die textfield- bzw. textarea-Elemente aus den Zeilen 19 bzw. 23 unterscheiden sich nur durch die Anzahl der Zeilen des Eingabefensters - eines für textfield, beliebig viele für die textarea.
scrolling_list aus Zeile 29 funktioniert ähnlich wie das popup_menü weiter oben, nur daß die Option -size die Anzahl der sichtbaren Einträge bestimmt (der Rest ist über eine Scrollbar erreichbar) und mehrere Einträge selektiert werden können, wenn -multiple auf 'true' steht.
Die checkbox_group aus Zeile 37 ähnelt der radio_group aus Zeile 13, nur daß sie auch mehrere Optionen gleichzeitig zur Auswahl zuläßt. Gibt's statt freier Auswahl nur Ja oder Nein, tut's auch die Einzel-Checkbox aus Zeile 44.
Der Submit-Button aus Zeile 50 dient zum Absenden des Formulars. Die -value-Option bestimmt seine Beschriftung. Der Browser übermittelt diesen Wert in der Variable, die über den -name-Eintrag definiert ist. So kann man serverseitig feststellen, welcher von eventuell mehreren Submit-Buttons gedrückt wurde.
Der Reset-Button kommt ohne Parameter aus, da er lediglich die Formularparameter auf die ursprünglich gesetzten Werte zurücksetzt, nachdem der Benutzer daran herumgespielt hat.
Ab Zeile 55 macht sich form.pl daran, den ganzen Sermon auszugeben, angefangen vom Header und der start_html-Sequenz. Die start_form-Routine beginnt die HTML-Formular-Defininition und setzt die Übertragungsmethode auf GET (Standard ist POST) und die -action, das aufzurufende CGI-Skript auf /cgi-bin/dump.pl - unser letztens vorgestelltes CGI-Debug-Skript.
Ab Zeile 62 verpackt form.pl die Formular-Bestandteile in eine zweispaltige Tabelle mit Rahmen und setzt end_form bzw. end_html dahinter, um Formular und HTML-Code sauber abzuschließen.
Ins cgi-bin-Verzeichnis des Web-Servers verfrachtet, liefert form.pl einem mit http://server/cgi-bin/form.pl darauf deutenden Browser das in Abbildung 1 dargestellte Bild. Drückt der Benutzer den Submit-Knopf mit der Aufschrift Absenden, kontaktiert der Browser das in der start_form-Routine gesetzte Skript cgi-bin/dump.pl nach der GET-Methode. Dieses gibt vor lauter Schreck die Werte nach Abbildung 3 aus.
Listing 1: form.pl |
1 #!/usr/bin/perl -w 2 3 use CGI qw/:standard :html3/; 4 5 %labels = ('r' => 'Rot', 'b' => 'Blau', 'g' => 'Grün'); 6 7 $popup_menu = popup_menu( 8 '-name' => 'farbe1', 9 '-values' => ['r', 'g', 'b'], 10 '-default' => 'r', 11 '-labels' => \%labels); 12 13 $radio_group = radio_group( 14 '-name' => 'farbe2', 15 '-values' => ['r', 'g', 'b'], 16 '-default' => 'r', 17 '-labels' => \%labels); 18 19 $textfield = textfield( 20 '-name' => 'farbe3', 21 '-default' => ''); 22 23 $textarea = textarea( 24 '-name' => 'farbe4', 25 '-default' => '', 26 '-rows' => 2, 27 '-columns' => 20); 28 29 $scrolling_list = scrolling_list( 30 '-name' => 'farbe5', 31 '-values' => ['r', 'g', 'b'], 32 '-default' => ['r', 'g'], 33 '-size' => 3, 34 '-multiple' => 'true', 35 '-labels' => \%labels); 36 37 $checkbox_group = checkbox_group( 38 '-name' => 'farbe6', 39 '-values' => ['r', 'g', 'b'], 40 '-default' => 'r', 41 '-linebreak' => 'true', 42 '-labels' => \%labels); 43 44 $checkbox = checkbox( 45 '-name' => 'farbe7', 46 '-checked' => 'checked', 47 '-value' => 'ja', 48 '-label' => 'Ja?'); 49 50 $submit = submit('-name' => 'submit_knopf', 51 '-value' => 'Absenden'); 52 53 $reset = reset(); 54 55 print header, 56 start_html('-title' => 'Form Example', 57 '-BGCOLOR' => '#e0e0e6'), 58 59 start_form('-method' => 'GET', 60 '-action' => '/cgi-bin/dump.pl'), 61 62 table({'border' => 1}, 63 TR(td(tt("popup_menu")), td($popup_menu)), 64 TR(td(tt("radio_group")), td($radio_group)), 65 TR(td(tt("textfield")), td($textfield)), 66 TR(td(tt("textarea")), td($textarea)), 67 TR(td(tt("scrolling_list")), td($scrolling_list)), 68 TR(td(tt("checkbox_group")), td($checkbox_group)), 69 TR(td(tt("checkbox")), td($checkbox)), 70 TR(td(tt("submit")), td($submit)), 71 TR(td(tt("reset")), td($reset)), 72 ), 73 end_form, 74 end_html; |
![]() | ![]() |
Abb.1: Ausgabe von form.pl | Abb.2: ... nach dem Absenden |
Ungeachtet ob ein Parameter varname via die GET oder die POST-Methode zum CGI-Skript gelangt, liest
$val = param('varname')
den übermittelten Wert aus. Dabei erledigt CGI.pm automatisch die Dekodierung maskierter Spezialzeichen. Im Falle von Listen mit multipler Auswahl liefert
@list = param('multvarname');
eine Liste gesetzter Werte.
Das sagte immer der Showmaster von ``Hopp oder Top!'', eine einst auf Tele 5 ausgestrahlte Quizsendung, in der ich eines Tages die Ehre hatte, mitzuspielen. Leider verlor ich unglücklich gegen eine Hausfrau aus dem Münchner Umland und zog grummelnd mit dem Trostpreis, einem halben Dutzend mit Simpsons-Zeichentrick-Motiven bedruckter Socken ab. Egal!
Mangels Showkarriere stelle ich heute also das vereinfachte Order-System aus Listing shop.pl vor, dessen erste Seite zur Eingabe einer Kundennummer auffordert (siehe Abb. 3). Ein Klick auf den Auf geht's-Knopf übermittelt diese an den Server, der wiederum eine Seite mit einer Auswahl von drei Produkten zurückliefert (siehe Abb. 4). Entscheidet sich der Benutzer für eines, indem er den entsprechenden Radio-Button selektiert und den Bestellen!-Knopf drückt, schreibt shop.pl
Kundennummer: 12345 Bestellung: Prodigy, The Fat of the Land, DM 19,90
in die Datei orders.txt im cgi-bin-Verzeichnis und zeigt dem Besteller die dritte Seite mit ein paar Dankesworten an (siehe Abb. 5).
Woher weiß shop.pl die Nummer des Kunden, der die Bestellen!-Taste auf der zweiten Seite drückte? Die erste Seite ist lange weg, und auch der Server weiß von nichts. Die Lösung: Kaum nimmt das Skript die Nummer aus dem ersten Formular entgegen, schmuggelt Zeile 31 sie mittels eines Hidden Fields in das Bestellformular, sodaß der Kunde seine Nummer unbewußt mitschickt, wenn er den Bestellen!-Knopf betätigt.
Das Hidden Field schleppt also die Kundennummer unsichtbar von der ersten Seite zur dritten.
Der flock-Befehl aus Zeile 35 sichert sich das Exklusiv-Recht auf die Datei orders.txt. Kein zweiter Prozeß oder Thread darf das zur gleichen Zeit. Da CGI-Skripts oft parallel ablaufen, ist diese Sicherung notwendig, gleichzeitige Schreiber könnten sonst das Dateiformat zerstören.
Hier muß ich noch erzählen, daß CGI.pm tatsächlich so etwas wie Status-Informationen behält: Muß es ein HTML-Formular malen, und stellt fest, daß der Name eines Feldes bereits im Eingabe-Bereich vorliegt, setzt es den voreingestellten Wert des Feldes auf den empfangenen Wert. Im Fall des HIDDEN-Felds kundennummer hat dies zur Folge, daß
print hidden(-name => 'kundennummer');
auch wie
print hidden(-name => 'kundennummer', -value => param('kundennummer');
reagiert und automatisch die Kundennummer weiterreicht.
Damit der Besteller bei eventuell auftretenden Fehlern nicht das unschöne Internal Server Error zu sehen bekommt, steht der Hauptteil des Skripts ab Zeile 11 in einem eval-Konstrukt, Abstürze dazwischen landen in Zeile 52, die eine Vertröstungs-Meldung ausgibt und die wahre Fehlerursache in /tmp/errorlog samt Datum festhält. So kommt der Systemadministrator (bei komplexeren Systemen als dem vorgestellten) sporadisch auftretenden Fehlern auf die Schliche.
Schluß für heute! Nächstes Mal kommen Cookies und Server-seitiges Status-Speichern dran. See ya!
Listing 2: shop.pl |
1 #!/usr/bin/perl -w 2 3 use CGI qw/:standard/; 4 5 print header; 6 7 %labels = (1 => 'Prodigy, The Fat of the Land, DM 19,90', 8 2 => 'Rolling Stones, Bridges of Babylon, DM 18,90', 9 3 => 'Green Day, Nimrod, DM 21,90'); 10 11 eval { 12 if(!defined param('kundennummer')) { ### Seite 1 13 print start_html('-title', 'Willkommen'), 14 h1('Willkommen im Cyber-Shop!'), 15 start_form, 16 p("Ihre Kundennummer:"), 17 textfield(-name => 'kundennummer'), 18 submit(-value => "Auf geht's!"), 19 end_form, end_html; 20 } elsif(!defined param('bestellung')) { ### Seite 2 21 print start_html('-title', 'Bestellung'), 22 h1("Unsere Preis-Schlager!"), 23 start_form, 24 radio_group('-name' => 'bestellung', 25 '-values' => [1,2,3], 26 '-default' => 1, 27 '-linebreak' => 'true', 28 '-labels' => \%labels), 29 p, 30 submit(-value => 'Bestellen'), 31 hidden(-name => 'kundennummer'), 32 end_form, end_html; 33 } else { ### Seite 3 34 open(ORDER, ">>orders.txt") || die "Cannot open orders.txt"; 35 flock(ORDER, 2); 36 print ORDER "Kundennummer: ", param('kundennummer'), 37 " Bestellung: ", $labels{param('bestellung')}, "\n"; 38 close(ORDER); 39 40 print start_html('-title', 'Danke!'), 41 p, "Vielen Dank. ", 42 i($labels{param('bestellung')}), 43 " geht Ihnen in den nächsten Tagen zu.", p, 44 p, "Der fällige Betrag wird mit Kundennummer ", 45 param('kundennummer'), " verrechnet.", 46 p, "Viel Spaß damit!", 47 start_form, submit(-value => "Zurück zum Eingang"), 48 end_form, end_html; 49 } 50 }; 51 52 if ($@) { ### Fehler 53 print "Unser System kann Ihre Order leider momentan " . 54 "nicht entgegennehmen. Bitte versuchen Sie es später " . 55 "nochmal.\n"; 56 open(ERRORLOG, ">>/tmp/errorlog"); 57 print ERRORLOG scalar localtime, "> $@"; 58 close(ERRORLOG); 59 } |
Der Autor |
Michael Schilli arbeitet als Web-Engineer für AOL/Netscape in Mountain View, Kalifornien. Er ist Autor des 1998 bei Addison-Wesley erschienenen (und 1999 für den englischsprachigen Markt als "Perl Power" herausgekommenen) Buches "GoTo Perl 5" und unter michael@perlmeister.com oder http://perlmeister.com zu erreichen. |
Copyright © 1998 Linux-Magazin Verlag