zurückInhaltvorwärts Perl Einführung: Kontrollstrukturen

12.5 Das CGI.pm Modul

Bevor es losgeht, möchten wir noch anmerken, daß wir hier elementare HTML-Kenntnisse voraussetzen. Diese könnt ihr Euch z.B. bei www.teamone.de anlesen.

Was ist CGI?
CGI steht für Common Gateway Interface und beschreibt einen Standard, um externe Programme von einem Webserver aus zu starten. Extern heißt hier, daß das aufgerufene Programm nicht Teil des Webservers selbst ist, sondern erst einmal irgendeine Applikation, die sich z.B. auf der Platte des Servers befindet. Dies ist allerdings nicht alles. Zusätzlich beschreibt der CGI Standard wie Parameter, also z.B. Formulareingaben, über den Webserver an die oben bereits erwähnte externe Applikation weitergegeben werden. Bei den erwähnten externen Programmen handelt es sich z.B. um den Perl Interpreter, der ein Skript ausführt, das z.B. Formulareingaben verarbeitet. Diese Verarbeitung kann z.B. das Abspeichern in eine Datenbank bedeuten.

Bezüglich der Art und Weise wie die Daten an den Web Server übermittelt werden gibt es zwei Methoden: Die GET und die POST Methode. Wenn man im Browser auf einen Submit Knopf drückt, generiert der Browser eine HTTP Anfrage, die zurück an den Webserver geschickt wird. Diese HTTP Anfrage enthält die im Formular eingegebenen Daten und weitere Information. Bei der GET Methode werden die Formulareingaben einfach an die URL der HTTP Anfrage angehängt. Das sieht in etwa folgendermaßen aus. Der Web Server erhält folgende Anfrage, die mit dem Befehl GET beginnt.

GET /scripts/tuewas.pl?vorname=Marco&nachname=Marzorati&iq=3000 HTTP/1.0

Wenn der Web Server diese Anfrage bekommt führt er das Skript tuewas.pl im Verzeichnis /scripts mit den Variabeln vorname, nachname und iq aus. Diese Variablen sind im entsprechenden HTML Formular definiert und durch den Benutzer mit Marco, Marzorati und 3000 im Formular belegt worden. Man spricht allerdings hier nicht von Variabeln, sondern von name/value-Paaren. Wie ihr sehen könnt werden sie in der Form

vorname=Marco&nachname=Marzorati&iq=3000

übergeben. Das ? nach dem Skriptnamen und dient wie die & als Trennzeichen. Am Schluß erfolgt die Angabe der Version des HTTP Protokolls. Dies braucht der Web Server, um zu wissen, ob er die Anfrage überhaupt verstehen kann (vielleicht unterstützt der Web Server kein HTTP 1.0, ist aber sehr, sehr unwahrscheinlich) bzw. ob sie überhaupt richtig formuliert ist. Diese name/value Strings habt ihr sicherlich schon mal bei Suchmaschinen oben in Feld für die URL eurer Browser gesehen.

Wie sieht das nun bei der POST Methode aus? Bei der POST Methode werden die Formulareingaben im BODY Teil der HTTP Anfrage übertragen (alles klar?). Der Web Server erhält folgenden Befehl:

 POST /scripts/tuewas.pl HTTP/1.0
<weiteres HTTP Header Zeug, Infos>
<noch mehr HTTP Header Zeug, Infos>
vorname=Marco&nachname=Marzorati&iq=3000

Welche Methode man anwendet kommt auf den Einzelfall an.

Was macht der Web Server jetzt, wenn er diese ganzen Infos bekommt? Bei der GET Methode packt er die name/value-Paare in eine durch den CGI Standard definierte Umgebungsvariable. Diese Umgebungsvariable lautet QUERY_STRING. Auf sie kann über ein CGI Programm, z.B. ein Perl Skript, zugegriffen werden (es sein hier erwähnt, daß der Web Server entsprechend konfiguriert sein muß. Gleich mehr dazu). Bei der POST Methode kommen die name/value-Paare über den Standard Input. Für eine CGI Applikation ist Standard Input natürlich nicht die Tastatur (!), sondern (wischi,waschi Erklärung; ist auch nicht weiter wichtig) ein entsprechender Datenstrom vom Webserver. Übrigens, Standard Output für ein CGI Skript ist natürlich nicht der Bildschirm (des Servers). Vielmehr wird z.B. ein in Perl abgesetzter print Befehl vom Webserver an den Browser des Clients weitergeleitet.

Neben der Umgebungsvariablen QUERY_STRING werden durch den CGI Standard eine ganze Reihen von anderen Umgebungsvariablen definiert, auf die dann auch die entsprechende CGI Applikation Zugriff hat. Da sind ganz interessante Infos dabei. Viele Leute wissen gar nicht, was sie so alles verraten. Hier ein kleiner Auszug. Wir verraten gleich wie man zu einer etwas vollständigeren Liste kommt.

HTTP_USER_AGENT Browser-Typ des Clients
HTTP_FROM Email Adresse des Benutzers (wird nicht immer unterstützt)
REMOTE_ADDR IP Adresse Clients
CONTENT_LENGTH Länge des name/value Strings

Wie greift nun ein als CGI Applikation fungierendes Perl Skript auf die Umgebungsvariabeln und anderen Informationen zu? Zuerst einmal muß der Web Server für Perl korrekt konfiguriert werden, den sonst versucht der Browser das CGI Programm anzuzeigen, bzw. auf die Eigene Festplatte herunterzuladen.

Beim Internet Information Server 3.0 unter Windows NT, muß hierzu ein Eintrag in die Registry unter HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\Script Map\ erfolgen. Hier muß eine neue Zeichenfolge mit dem Namen ".pl" eingefügt werden. Als Wert wird der Pfad zum Perl-Interpreter angegeben, gefolgt von %s %s. Also z.B.

C:\Perl\bin\Perl.exe %s %s

Am besten danach gleich den Rechner booten, da Windoof auch nach starten und stoppen des WWW Dienstes nichts auf die Kette kriegt. Es sei hier angemerkt, daß man Perl aus Sicherheitsgründen NICHT in die WWW Root installieren sollte bzw. Perl.exe nicht in die WWW Root legen sollte. Da bei NT bei jeder neuen Anfrage eines Browers eine neue Instanz von Perl.exe gestartet wird, kann man auf diese Art und Weise seinen Rechner ganz schnell in die Knie zwingen. Aus diesem Grunde empfiehlt es sich bei der Perl Installation auf einem Webserver Perl for ISAPI zu installieren (siehe Perl Dokumentation). Für Sicherheitshinweise sei auf einen Artikel in der Bibel (also c't) verwiesen. In einer der '98er Ausgaben erschien ein längerer Artikel hierzu. Einfach mal schauen (die c't sollte jeder lesen :-)).

Das CGI.pm Modul vereinfacht die CGI Programmierung ganz erheblich. Mit diesem Modul entfällt z.B. das sonst notwendige Lesen und anschließende Zerhacken von Query-Strings, um an die durch HTML Formulare übergebenen Informationen zu gelangen, die in name/value Paaren der Form Name1=Wert1&Name2=Wert2 übergeben werden. Für die, die sich noch an diese Zeiten erinnern:

Also was muß das Skript nun tun, um auf die erforderlichen Informationen zuzugreifen? Als echter Mann benutzt man einfach den folgenden Code-Auschnitt. (Frage für Schlaue: Wurde hier GET oder POST für die HTTP Anfrage benutzt?).

 

# lesen von Standard Input. Die Umgebungsvariablen sind im Hash %ENV abgelegt.
# Also CONTENT_LENGTH Anzahl von Zeichen werden von Standard Input in $buffer gelesen..
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});

#
# Die Formulardaten werden nach einzelnen Eingabefelder
# getrennt (Trennzeichen &)
# Die Daten liegen im Array Pairs vor, jedes Element
# enhaelt 1 Formularfeld (Name=Wert)
#
# Name1=Wert1&Name2=Wert2
#

@pairs = split(/&/, $buffer);

#
# Nun werden die codierten Sonderzeichen umgewandelt und die Name/Wert Paare
# getrennt und in dem Hash %Form gespeichert, auf den dann später zugegriffen werden kann.
# Später sieht das dann so aus:
#
# $Form{Name1} = Wert1
# $Form{Name2} = Wert2
#

foreach $pair (@pairs) {
    ($name, $value) = split(/=/, $pair); # Gleichheitszeichen rausnehmen
    $value =~ tr/+/ /; # Leerzeichen werden im String als + dargestellt.
                       # Jetzt werden die + entsprechend wieder durch Leerzeichen ersetzt.
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # Sonderzeichen (z.B. ä,ü,&,\ etc.) liegen
    								 # im String in Hex Code vor und werden
								 # jetzt wieder in ASCII umgewandelt
    $value =~ s/~!/ ~!/g;					 # Zur Sicherheit!
    $Form{$name} = $value;
}

# ..... tue dies und das mit denen in %Form gespeicherten Formulareingaben.

Alles klar?! Mit CGI.pm geht das viel zivilisierter ab. Dennoch sollte sich jeder einmal die Mühe machen, den obigen gar nicht so schweren Code nachzuvollziehen.

Als Beispiel für eine CGI Anwendung schreiben wir einen kleinen Pizza Shop mit einer riesigen Auswahl. Zuerst der HTML Code für die Startseite:

<HTML>
<HEAD>
<TITLE>TEK-PIZZA</TITLE>
</HEAD>
<BODY>
<H1 ALIGN="CENTER">TEKROMANCER Pizza Bestellservice </H1>
Klicken Sie auf <A HREF="/scripts/cgipm/formular.pl">weiter</A>, um eine
Pizza zu bestellen.
</BODY>
</HTML>

Die Seite enthält nur einen Link auf ein Skript, welches das eigentliche Eingabeformular erzeugt. Man beachte den HTML Code-Abschnitt, der den Link auf das CGI-Skript enthält:

<A HREF="/scripts/cgipm/formular.pl">

Wenn man auf den Link klickt wird formular.pl aufgerufen. Dieses Skript erzeugt das Formular, auf dem man sich die Pizza zusammenstellen kann:


use CGI;

# formular.pl erzeugt ein Pizza Bestellformular


$cgi = new CGI;

# Die nächste Zeile erzeugt den HTTP Header
print $cgi->header();

# Die folgende Zeile übernimmt den folgenden HTML Code, mit dem jede HTML Seite anfängt:
# <HTML><HEAD><TITLE>Pizza Bestellung</TITLE></HEAD><BODY>
print $cgi->start_html("Pizza Bestellung");

print $cgi->h1("Stellen Sie sich Ihre Pizza aus unserer Riesen-Auswahl zusammen!");


# HTML Formular erzeugen

print $cgi->startform(-action => '/scripts/cgipm/bestellen.pl',
                 -method => 'POST'
                 );

print $cgi->h2("Größe");

# Groesse der Pizza
print $cgi->radio_group(-name=>'groesse',
                        -values=>['klein','gross','wagenrad'],
                        -default=>'klein',
                        -linebreak=>'true'
                       );


# Zutaten
print $cgi->h2("Zutaten");
print $cgi->checkbox(-name=>'Salami',
                     -value=>'Salami',
                     -label=>'Salami'
                    );                       
print $cgi->checkbox(-name=>'Kaese',
		     -checked=>'checked',
                     -value=>'Kaese',
                     -label=>'Kaese'
                    );                       
print $cgi->checkbox(-name=>'Thunfisch',
                     -value=>'Thunfisch',
                     -label=>'Thunfisch'
                    );
      
print $cgi->checkbox(-name=>'Pilze',
                     -value=>'Pilze',
                     -label=>'Pilze'
                    );

print $cgi->checkbox(-name=>'Spinat',
                     -value=>'Spinat',
                     -label=>'Spinat'
                    );
print $cgi->br();
                    

# Getraenke
print $cgi->h2("Getränke");
print $cgi->scrolling_list(-name=>'Getraenke',
                             -values=>['Kein','Cola','Wasser','Bier','Fusel'],
                             -size=>1,
                            );

print $cgi->p();
                            
# Buttons
print $cgi->submit("OK");
print " ";
print $cgi->reset('Löschen');


print $cgi->endform();                     

# Die folgende Zeile liefert den HTML Code zum Abschluß der HTML Seite.
# Hier werden der BODY und der HTML Tag, die am Anfang mit start_html()
# geöffnet wurden, wieder geschlossen. D.h. .
print $cgi->end_html();

Hat man sich mit entsprechenden Klicks die Super-Pizza zusammengestellt drückt man auf den Submit-Knopf und startet das nächste Skript, bestellen.pl. Dieses Skript zeigt die bestellte Pizza an und erzeugt ein Formular, in dem man seine Adresse eintragen kann (macht durchaus Sinn die Adresse anzugeben, damit man die Pizza auch bekommt).


use CGI;

$form = new CGI;
$groesse = $form->param("groesse");
$salami = $form->param("Salami");
$kaese = $form->param("Kaese");
$thunfisch = $form->param("Thunfisch");
$spinat = $form->param("Spinat");
$pilze = $form->param("Pilze");
$getraenk = $form->param("Getraenke");

print $form->header();
print $form->start_html("Ihre Bestellung");
print $form->h1("Ihre Bestellung");

print $form->table({-border=>1},
	$form->Tr({-align=>CENTER,-valign=>TOP},
               [
                     $form->th(['Größe', 'Belag','Getränk']),
                     $form->td(["$groesse" , "$salami $kaese $thunfisch $spinat $pilze", "$getraenk"])
               ]
           )
        );


print $form->h4("Bitte geben Sie Ihre Adresse ein, um die Bestellung zu bestätigen.");

print $form->startform(-action => '\scripts\cgipm\bestaetigen.pl',
		       -method => 'POST'
		      );

print $form->table({-border=>0},
	$form->Tr({-align=>LEFT,-valign=>TOP},
               [
                     $form->td(['Vorname', $form->textfield(-name=>'Vorname', -size=>30, -maxlength=>30), "", ""]),
                     $form->td(["Nachname" , $form->textfield(-name=>'Nachname', -size=>30, -maxlength=>30), "", ""]),
                     $form->td(["Straße" , $form->textfield(-name=>'Strasse', -size=>30, -maxlength=>30), "", ""]), 
                     $form->td(["Postleitzahl" , $form->textfield(-name=>'PLZ', -size=>5, -maxlength=>5), "Ort", $form->textfield(-name=>'Ort', -size=>20, -maxlength=>20)])
               ]
           
            )       
       );


print $form->p();

print $form->submit("Bestellen");
print " ";
print $form->reset("Löschen");

print $form->hidden(-name => 'Bestellung',
                    -value => "$groesse, $spinat, $salami, $thunfisch, $kaese, $pilze, $getraenk"
                    );

print $form->endform();
print $form->end_html();

Drückt man nach Eingabe seiner Adresse schließlich auf den Knopf "Bestellung" im Formular wird das letzte Skript, bestaetigen.pl, aufgerufen. Dieses Skript schickt eine Bestätigung der Bestellung an den Browser und speichert die Bestellung selbst in einer Datei auf dem Server ab. Statt einer Datei kann man natürlich auch eine Datenbank nehmen (z.B. Access, MySql oder Oracle) oder eine Mail mit der Bestellung mittels des SMTP Moduls an den Pizza Bäcker schicken.



use CGI;

$form = new CGI;

$vorname = $form->param("Vorname");
$nachname = $form->param("Nachname");
$strasse = $form->param("Strasse");
$plz = $form->param("PLZ");
$ort = $form->param("Ort");
$bestellung = $form->param("Bestellung");

print $form->header();
print $form->start_html("Bestätigung");
print $form->h1("Vielen Dank für Ihre Bestellung");
print "Innerhalb von 30 Minuten schicken wir Ihnen Ihre Pizza an folgende Adresse:

"; print "$vorname $nachname
"; print "$strasse
"; print "$plz $ort
"; print $form->end_html(); open (FILE, ">>pizza.txt"); print FILE "$bestellung\n"; print FILE "$vorname $nachname\n"; print FILE "$strasse\n"; print FILE "$plz $ort\n"; print FILE "--------------------------------\n\n"; close (FILE);
Perl Einführung: Kontrollstrukturen zurückInhaltvorwärts