Heute, im zweiten Teil, geht's um Formulare und ein simples
Online-Order-System. Ein CGI-Skript kann mit CGI.pm dem Client
nicht nur den notwendigen HTML-Code senden, sondern
sogar die eingegebenen Parameter in Empfang nehmen und wiederum
HTML zurückschicken.
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 einen 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.
01 #!/usr/bin/perl -w
02 ######################################################################
03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
04 ######################################################################
05
06 use CGI qw/:standard :html3/;
07
08 %labels = ('r' => 'Rot', 'b' => 'Blau', 'g' => 'Grün');
09
10 $popup_menu = popup_menu( ### Popup-Menü
11 '-name' => 'farbe1', # Name des Feldes
12 '-values' => ['r', 'g', 'b'], # Einzelwerte
13 '-default' => 'r', # Voreingestellt
14 '-labels' => \%labels); # Wert -> angezeigter Name
15
16 $radio_group = radio_group( ### Gruppe von Radio
17 '-name' => 'farbe2', # Name des Feldes
18 '-values' => ['r', 'g', 'b'], # Einzelwerte
19 '-default' => 'r', # Vorausgewählt
20 '-labels' => \%labels); # Name -> angezeigter Name
21
22 $textfield = textfield( ### Einzeiliger Text
23 '-name' => 'farbe3', # Name des Feldes
24 '-default' => ''); # Ist anfangs leer
25
26 $textarea = textarea( ### Mehrzeiliger Text
27 '-name' => 'farbe4', # Name des Feldes
28 '-default' => '', # Ist anfangs leer
29 '-rows' => 2, # Zwei Zeilen
30 '-columns' => 20); # 20 Zeichen breit
31
32 $scrolling_list = scrolling_list( ### Scrollbare Liste
33 '-name' => 'farbe5', # Name des Feldes
34 '-values' => ['r', 'g', 'b'], # Wählbare Werte
35 '-default' => ['r', 'g'], # Vorselektiert
36 '-size' => 3, # Höhe der Box
37 '-multiple' => 'true', # Multiple Auswahl OK
38 '-labels' => \%labels); # Name -> angezeigter Name
39
40 $checkbox_group = checkbox_group( ### Gruppe von Schaltern
41 '-name' => 'farbe6', # Name des Feldes
42 '-values' => ['r', 'g', 'b'], # Einzelwerte der Schalter
43 '-default' => 'r', # 1. Schalter gedrückt
44 '-linebreak' => 'true', # Untereinander aufreihen
45 '-labels' => \%labels); # Name -> angezeigter Name
46
47 $checkbox = checkbox( ### Einzelknopf
48 '-name' => 'farbe7', # Name des Feldes
49 '-checked' => 'checked', # Vorgewählt
50 '-value' => 'ja', # Wert falls gedrückt
51 '-label' => 'Ja?'); # Dargestellter Text
52
53 $submit = submit( ### Sende-Knopf
54 '-name' => 'submit_knopf', # Name des Feldes
55 '-value' => 'Absenden'); # Beschriftungstext und gelie-
56 # ferter Wert falls ausgelöst
57
58 $reset = reset( ### Reset-Knopf
59 '-value' => 'Zurücksetzen'); # Beschriftungstext
60
61 print header, # Alles als HTML ausgeben
62 start_html('-title' => 'Form Example',
63 '-BGCOLOR' => '#e0e0e6'),
64
65 start_form('-method' => 'GET', # Formular-Anfang und Aktions-URL
66 '-action' => '/cgi-bin/dump.pl'),
67
68 table({'border' => 1}, # Tabelle mit Formularelementen
69 TR(td(tt("popup_menu")), td($popup_menu)),
70 TR(td(tt("radio_group")), td($radio_group)),
71 TR(td(tt("textfield")), td($textfield)),
72 TR(td(tt("textarea")), td($textarea)),
73 TR(td(tt("scrolling_list")), td($scrolling_list)),
74 TR(td(tt("checkbox_group")), td($checkbox_group)),
75 TR(td(tt("checkbox")), td($checkbox)),
76 TR(td(tt("submit")), td($submit)),
77 TR(td(tt("reset")), td($reset)),
78 ),
79 end_form, # Formularende
80 end_html; # HTML-Ende
|
|
| 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!
|
| Abb.3: Startseite des Order-Systems |
|
| Abb.4: Bestellen ... |
|
| Abb.5: ... fertig! |
shop.pl
01 #!/usr/bin/perl -w
02 ######################################################################
03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
04 ######################################################################
05
06 use CGI qw/:standard/; # Cgi-Funktionen
07 use Fcntl qw/:flock/; # LOCK_EX
08
09 print header(); # Header ausgeben
10
11 %products = (1 => 'Perl für Frauen, O\'Reilly, DM 59.90', # Produkte
12 2 => 'Perl in 3 Tagen, SamsNet, DM 18.90',
13 3 => 'Go To Perl5, AWL, DM 59.00');
14
15 eval { # Fehler abfangen
16
17 if(!defined param('kundennummer')) { # Keine Kundennummer?
18 print start_html('-title', 'Willkommen'), # -> Startseite
19 h1('Willkommen im Perl-Buchladen!'),
20 start_form(), "Ihre Kundennummer:",
21 textfield(-name => 'kundennummer'),
22 submit(-value => "Einkaufen geh'n!"),
23 end_form(), end_html();
24
25 } elsif(!defined param('bestellung')) { # Keine Bestellung?
26 print start_html('-title', 'Bestellung'), # -> Bestellseite
27 h1("Unser Sortiment:"), start_form(),
28 checkbox_group(
29 '-name' => 'bestellung',
30 '-values' => [keys %products],
31 '-linebreak' => 'true', # Untereinander
32 '-labels' => \%products), # Produkte
33 p(), submit(-value => 'Bestellen'), # Bestellknopf
34 hidden(-name => 'kundennummer'), # Weiterreichen
35 end_form(), end_html();
36
37 } else { # Bestellung speichern
38 open(ORDER, ">>orders.txt") || die "Cannot open orders.txt";
39 flock(ORDER, LOCK_EX); # Lock setzen
40 print ORDER "Kundennummer: ", param('kundennummer'),
41 " Bestellung: ", $products{param('bestellung')}, "\n";
42 close(ORDER);
43
44 print start_html('-title', 'Danke!'), # Danke!-Seite
45 p(), "Vielen Dank. ",
46 i($products{param('bestellung')}),
47 " geht Ihnen in den nächsten Tagen zu.", p(),
48 p(), "Der fällige Betrag wird mit Kundennummer ",
49 param('kundennummer'), " verrechnet.",
50 p(), "Viel Spaß damit!",
51 start_form(), submit(-value => "Zurück zum Eingang"),
52 end_form(), end_html();
53 }
54 };
55
56 if ($@) { # Fehler?
57 print "Unser System kann Ihre Order leider momentan nicht " .
58 "entgegennehmen. Bitte versuchen Sie es später nochmal.\n";
59 open(ERRORLOG, ">>/tmp/errorlog");
60 print ERRORLOG scalar localtime, "> $@";
61 close(ERRORLOG);
62 }
![]() |
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |