2Grundlagen der Web- Programmierung mit Python

Werbung
2
Grundlagen der WebProgrammierung mit Python
In diesem Kapitel geht es um die Grundlagen der Web-Programmierung. Im Mittelpunkt
stehen Protokolle der Datenkommunikation und Python-Programmiertechniken, die
direkt auf die Anwendung dieser Protokolle bezogen sind. Wenn Sie mit TurboGears
Websites entwickeln, arbeiten Sie meist auf einem abstrakteren Niveau und werden
einige der hier beschriebenen Python-Module nur selten verwenden. Dennoch sollte
man sicherlich die Basis der Webtechnologie kennen, um zu wissen, was man tut. Häufig
hilft es, die Dinge besser zu verstehen, wenn man in kleinen Programmierexperimenten
die Anwendung eines Protokolls ausprobiert.
Ein weiterer Schwerpunkt in diesem Kapitel ist die Programmierung von CGI-Skripten
mit Python. Das sind Python-Programme, die über einen HTTP-Server aufgerufen werden und eine Webseite zurückgeben. Auch wenn Sie mit CherryPy und TurboGears
modernere Techniken verwenden und komplette Server programmieren, gibt es auch im
Web-2.0-Zeitalter immer noch Anwendungen für CGI.
2.1
Was ist ein Protokoll?
Die Kommunikation zwischen Rechnern, die über das Internet miteinander verbunden
sind, wird durch Protokolle gesteuert. Der Begriff stammt ursprünglich aus der Diplomatie und meint ein Regelwerk, das den Ablauf einer Kommunikation (z.B. zwischen
Staatsoberhäuptern) festlegt. Protokolle gibt es für alle Aspekte der Datenkommunikation. Für die Web-Programmierung sind vor allem Standards aus der Verarbeitungsschicht, der obersten Schicht des TCP/IP-Referenzmodells, relevant – und zwar insbesondere das Hypertext Transport Protocol (HTTP).
Schichten des
TCP/IP-Referenzmodells
Protokolle
Verarbeitung
FTP, TELNET, SMTP, POP3, HTTP
Transport
TCP, UDP
Internet
IP
Host-an-Netz
Abbildung 2.1: TCP/IP-Referenzmodell in der Darstellung von Andrew S. Tanenbaum (2003)
Rapid Web Development mit Python
21
2 – Grundlagen der Web-Programmierung mit Python
2.2
URI und URL
Das Akronym URI wurde 1990 vom W3-Konsortium ursprünglich als Abkürzung für
Universal Resource Identifier eingeführt. Inzwischen wurde daraus Uniform Resource
Identifier. Ein URI ist eine nach bestimmten syntaktischen Regeln aufgebaute Zeichenkette, die eine abstrakte oder physische Ressource im Internet identifiziert. Zu Ressourcen gehören statische Webseiten, die als HTML-Datei gespeichert sind, aber auch dynamisch generierte Webseiten. Der wichtigste Spezialfall eines URI ist der bekannte URL
(Uniform Resource Locator) für das Abrufen von Webseiten. URIs gibt es aber auch für
andere Zwecke, etwa den Zugriff auf eine Datenbank (siehe Abschnitt 4.4.1) oder Telefonnummern. Die Syntax von URIs wird in RFC 3986 (Januar 2005) beschrieben. In diesem Standard wird übrigens empfohlen, von nun an die Bezeichnung URL zu meiden
und stattdessen den allgemeineren Begriff URI zu verwenden. Beispiele für URIs sind:
ftp://ftp.fernuni-hagen.de/
http://www.python.org/index.html
tel:+49-2302-12345
mailto:[email protected]
Ein URI kann die Angabe einer Autorität enthalten, die für die Bearbeitung zuständig ist,
muss es aber nicht. Es gibt auch autoritätsfreie URIs, wie z.B. die Angabe einer Telefonnummer. Aber für die Web-Programmierung sind vor allem URIs mit Autoritätsangabe
interessant. Sie haben folgenden allgemeinen Aufbau:
scheme : // authority path [?query] [#fragment]
Dabei ist scheme der Name des Identifikationsschemas. Im Falle von HTTP ist der Name
des Schemas gleichzeitig der Name des angewendeten Protokolls, eben HTTP. Der Teil
authority bezeichnet die Instanz, die für die Verarbeitung der Anfrage zuständig ist, z.B.
einen HTTP-Server. Der Rest des URIs wird dann von eben dieser Autorität ausgewertet.
Die authority-Passage des URI hat folgendes Format:
[userinfo @] host [:port]
Der optionale Teil userinfo kann ein Benutzername sein, eventuell zusammen mit einer
Information, wie der Benutzer für den Zugang zur Ressource autorisiert werden kann
(z.B. über ein Passwort).
Der Host kann auf dreierlei Weise spezifiziert werden:
K IP-Literal in eckigen Klammern, z.B. [197.23.14.19] oder [1080:0:0:800:200C:417A]
K Vierteilige dezimale IP-Nummer (32 bit) in Punktnotation (IP-Version 4), z.B.
197.23.14.19
K Domain-Name des Hosts, der im Domain Name System registriert ist, z.B.
www.python.org
Meistens wird der Domain-Name (dritte Alternative) verwendet.
22
Das HTTP-Protokoll
Dem Hostnamen können ein Doppelpunkt und eine Dezimalzahl folgen, die den Port
angibt, der vom Server verwendet wird. Im HTTP-Schema ist die Portnummer 80 voreingestellt und kann im URI weggelassen werden, falls der Server davon nicht abweicht.
Der dritte Teil des URI ist eine hierarchisch organisierte Pfadangabe der Form /path/to/
something. Der Pfad identifiziert eine Ressource im Zuständigkeitsbereich der angesprochenen Autorität. Man denkt vielleicht an einen Pfad im Verzeichnisbaum des Servers.
Doch ist diese Vorstellung heute nicht mehr angemessen, da die Interpretation des Pfades allein Sache der Autorität ist. Ein CherryPy-Server z.B. ermittelt aus dem Pfad ein
Objekt und eine Methode und eventuell Parameter, aber in der Regel keinen Ort im Verzeichnisbaum, an dem eine Datei gespeichert ist (siehe Kapitel 3). In der Philosophie des
URI-Standards sollte man den Begriff Pfad besser einfach nur als Namen einer Ressource
verstehen.
Der vierte (optionale) Teil des URI beginnt nach einen Fragezeichen ? und endet spätestens vor einer Raute #. Er enthält einen Querystring, in dem Variablennamen mit Werten
in der Form name=value belegt sind. Mehrere solcher Zuordnungen sind durch das Zeichen & voneinander getrennt. Damit kann ein Client in einem Request Daten an den Server übermitteln (Beispiel in Abschnitt 2.5.1).
Der letzte (wiederum optionale) Teil eines URI bezeichnet ein Fragment der gewünschten Ressource. Er folgt nach einer Raute # und kann z.B. ein Label in einem HTML-Text
sein, das eine Textstelle markiert, die im Browserfenster zuoberst erscheinen soll.
2.3
Das HTTP-Protokoll
HTTP (Hypertext Transfer Protocol) ist das Protokoll für die Übertragung von Ressourcen aus dem World Wide Web (WWW). Ressourcen sind – abstrakt gesprochen – Wissenseinheiten wie Webseiten, Bilder oder Filme. Sie können in Dateien gespeichert sein
oder bei einer Anfrage dynamisch erzeugt werden.
Wie die meisten Netzwerkprotokolle verwendet HTTP ein Client-Server-Modell. Ein
HTTP-Client (z.B. ein Webbrowser) öffnet eine Verbindung zu einem entfernten HTTPServer und schickt ihm eine Anfrage (Request) in Form eines Textes, der gemäß dem
HTTP-Protokoll aufgebaut ist. Der Server schickt eine Antwort (Response) und schließt
die Verbindung. HTTP ist ein zustandsloses Protokoll, d.h., eine Verbindung bleibt nicht
über mehrere Dialogschritte bestehen. Jedes HTTP-Paket steht für sich und enthält alle
für die Übertragung erforderlichen Metadaten.
Die Formate für Request- und Response-Botschaften sind ähnlich. Es sind ASCII-Texte
mit folgendem Aufbau:
K Eine Startzeile (Initial Line),
K keine, eine oder mehrere Header-Zeilen,
K eine Leerzeile,
K ein optionaler Körper (Body), z.B. eine Datei, Anfrage-Daten oder das zurückgesendete Ergebnis einer Anfrage.
Rapid Web Development mit Python
23
2 – Grundlagen der Web-Programmierung mit Python
Als Zeilenwechselzeichen sollte CRLF verwendet werden, das sind zwei Sonderzeichen
mit den ASCII-Werten 13 und 10 (dezimal).
2.3.1
Startzeile einer Anfrage (Initial Request Line)
Request- und Response-Botschaften unterscheiden sich im Aufbau der Startzeile. Bei
Requests besteht sie aus drei Teilen:
K HTTP-Methode. Die am häufigsten verwendeten Methoden sind GET und POST. Der
Methodenname wird in Großbuchstaben geschrieben.
K Pfad der angeforderten Ressource. Der Pfad ist der Teil des URI, der hinter dem HostNamen folgt.
K Angabe der HTTP-Version (in Großbuchstaben), das ist entweder HTTP/1.0 oder HTTP/
1.1
Beispiel:
GET /info/index.html HTTP/1.0
Falls die Anfrage an einen Proxy-Server gesendet wird, muss die Startzeile an Stelle des
Pfades den kompletten URI der angeforderten Ressource enthalten. Beispiel:
GET http://www.python.org/index.html HTTP/1.1
2.3.2 Startzeile einer Antwort (Initial Response Line)
Die Startzeile einer HTTP-Antwort (auch Statuszeile genannt) besteht aus drei Teilen, die
durch Leerzeichen getrennt sind: Die HTTP-Version, die Status-Nummer (Response Status Code) und eine kurze verbale Erklärung (Reason Phrase). Beispiele sind
http/1.0 200 OK
oder
http/1.0 404 Not Found
Tabelle 2.1 in der Referenz am Ende des Kapitels enthält einige Beispiele für häufig vorkommende Statusnummern.
2.3.3
Header
Header-Zeilen im Kopf einer HTTP-Botschaft enthalten verschiedene Informationen,
z.B. über den Sender oder die Nutzdaten im Körper des Pakets. Jeder Header ist eine
Textzeile im Format header-name: value.
24
Serverseitige Web-Programmierung mit Python
Die Zeile wird mit CRLF abgeschlossen. Zwischen Doppelpunkt und Wert value dürfen
beliebig viele Leerzeichen oder Tabulatorzeichen kommen. Eine Zeile, die mit Leerzeichen beginnt, zählt noch zum Wert der vorigen Header-Zeile. Auf diese Weise können
lange Werte übersichtlich auf mehrere Zeilen verteilt werden.
HTTP 1.0 definiert 16 Header, wobei keiner verpflichtend ist. Üblich im Sinne der „Netiquette“ ist, dass im Kopf eines Request zumindest Angaben zum Absender (From:) und
zum Client-Programm, das den Request sendet (User-Agent:), enthalten ist. HTTP 1.1
beschreibt 46 Header, dabei ist Host: verpflichtend. Tabelle 2.2 auf Seite 43f. enthält eine
Übersicht mit den wichtigsten Headern.
2.4
Serverseitige Web-Programmierung
mit Python
Python bietet eine Reihe von Modulen für die Programmierung von HTTP-Servern und
-Clients. Wir beginnen mit der serverseitigen Programmierung, weil Sie einen selbstgeschriebenen Server anschließend gut zum Testen der clientseitigen Programme verwenden können.
2.4.1
Ein einfacher HTTP-Server
Ein HTTP-Server, der statische Seiten und CGI-Skripte verarbeitet, kann mit Hilfe der
Python-Module BaseHTTPServer und CGIHTTPServer in wenigen Zeilen geschrieben werden. Legen Sie für das Server-Programm ein eigenes Verzeichnis an, und darin Unterverzeichnisse für CGI-Skripte (cgi-bin) und statische Websteiten (static). Beispiel für eine Verzeichnisstruktur:
/httpserver
/cgi-bin
/static
Wenn Sie unter MS Windows arbeiten, müssen Sie darauf achten, dass der Pfad zu Ihrem
Projektverzeichnis Unix-kompatibel ist. D.h. insbesondere, dass die Verzeichnisnamen
keine Sonderzeichen (Umlaute etc.) oder Leerzeichen enthalten dürfen (wie z.B. in Eigene
Dateien).
Speichern Sie in das Unterverzeichnis /static eine beliebige HTML-Seite. Das Serverskript, das wir gleich besprechen, kommt in die Wurzel des kleinen Verzeichnisbaums –
im Beispiel also in das Verzeichnis /httpserver.
Das Serverprogramm erhält üblicherweise den Namen httpd.py. Der Buchstabe d am
Ende des Dateinamens steht für Dämon (daimon). Damit bezeichnet man alle ServerProgramme, die ständig im Hintergrund laufen und Anfragen bearbeiten.
Rapid Web Development mit Python
25
2 – Grundlagen der Web-Programmierung mit Python
# httpd.py
from BaseHTTPServer import HTTPServer
from CGIHTTPServer import CGIHTTPRequestHandler
serveradresse =("localhost", 8080)
server=HTTPServer(serveradresse,
CGIHTTPRequestHandler)
server.serve_forever()
#1
#2
#3
Skript 2.1
Kommentar #1: Die Serveradresse ist ein Tupel, bestehend aus dem Namen des Servers
(hier localhost) und dem Port, der eine Zahl zwischen 1 und 65535 sein muss. Standardmäßig erhalten HTTP-Server den Port 80. Unter Unix müssen Server-Programme mit
einer Portnummer unter 1024 mit Administratorrechten laufen. Um das zu vermeiden,
verwendet man häufig Port 8080 für HTTP-Server.
#2: Instanzierung eines Server-Objekts
#3: Start des Servers
2.4.2 Zugriff auf statische Ressourcen
Starten Sie das Skript, öffnen Sie einen Browser und geben Sie als URI http://localhost:8080/static/index.html an. Sie erhalten als Antwort die statische Webseite mit dem
zum Speicherort des HTTP-Servers relativen Pfad /static/index.html.
Abbildung 2.2: Zugriff auf eine statische Webseite
Im Kommandofenster Ihres Betriebssystems sehen Sie eine Meldung des Servers. Sie
besteht aus dessen Namen, dem Datum, dem empfangenen HTTP-Request und der Statuszeile (erste Zeile) des Response.
localhost - - [25/May/2007 12:41:24] "GET /static/index.html http/1.1" 200 -
Was muss der HTTP-Server in diesem Fall tun? Er entnimmt der ersten Zeile den relativen Pfad der gewünschten Datei, öffnet die Datei, liest ihren Inhalt, fügt vorne einige
Header-Zeilen hinzu und sendet diesen Text (das HTTP-Response-Paket) an den Client
zurück.
26
Serverseitige Web-Programmierung mit Python
2.4.3 CGI-Skripte
Ressourcen des Internets können statische Dateien sein, sie können aber auch erst „just in
time“ als Reaktion auf eine Anfrage von einem Programm dynamisch erzeugt werden.
Ein Standard für die Handhabung dynamischer Webseiten ist das Common Gateway
Interface (CGI). Wenn in einem Request eine Referenz auf ein CGI-Skript vorkommt,
dann sendet der HTTP-Server nicht die im Pfad angegebene Datei zurück. Stattdessen
startet er das im URI spezifizierte Programm – das CGI-Skript. Dieses produziert eine
Antwort – einschießlich eines Headers, der den Typ des Inhalts angibt –, gibt sie an den
HTTP-Server zurück, der sie mit einer Startzeile zu einem HTTP-Paket komplettiert und
an den Client weiterleitet. Wie aber erkennt der HTTP-Server, ob eine statische oder
dynamische Ressource gewünscht wird? Die CGI-Skripte müssen in bestimmten Verzeichnissen gespeichert sein. Voreingestellt sind bei dem HTTP-Server aus Skript 1 die
Verzeichnisnamen /cgi-bin und /htbin. Alle Requests, bei denen der Pfad mit /cgi-bin
oder /htbin beginnt, werden als Anfragen an CGI-Skripte interpretiert.
Jedes CGI-Skript, das in Python geschrieben worden ist, muss zumindest zwei Komponenten enthalten. In der ersten Zeile (Magic Line) steht ein Hinweis an das Betriebssystem, welcher Interpreter zur Ausführung des Skriptes aufgerufen werden soll. Diese
Zeile muss mit den Zeichen #! beginnen. Dahinter steht ein Pfad zur ausführbaren Programmdatei des Python-Interpreters.
Bei einer Standardinstallation auf einem UNIX-Rechner lautet die erste Zeile:
#!/usr/bin/python
Bei MS Windows können Sie eine der beiden folgenden Möglichkeiten verwenden,
sofern Sie Python 2.4 standardmäßig installiert haben:
#!c:\python24\python.exe
#!/python24/python.exe
Zweitens enthält ein CGI-Skript eine print-Anweisung, mit der ein Text über die Standardausgabe sys.stdout ausgegeben wird. Dieser Text wird vom HTTP-Server gelesen
und zu einem vollständigen HTTP-Response an den Client weiterverarbeitet. Die Ausgabe des CGI-Skriptes besteht aus drei Teilen:
K Eine Header-Zeile, die den Typ der Nutzdaten definiert. In unseren Beispielen handelt es sich immer um HTML-Text. Der Header lautet dann:
Content-Type: html/text
Beachten Sie, dass am Ende der Headerzeile keine Leerzeichen stehen dürfen. Der
Zeilenumbruch muss unmittelbar hinter dem letzten Buchstaben stehen.
K Eine Leerzeile. Sie muss wirklich leer sein, darf also z.B. keine Leerzeichen enthalten.
K Der Body der HTTP-Nachricht. In der Regel ist das HTML-Text.
Das folgende CGI-Skript realisiert eine Digitaluhr. Abbildung 2.3 zeigt die Ansicht des
dynamisch erzeugten HTML-Dokuments in einem Browser. Am URL im Adressfenster
des Browsers erkennen Sie, dass das Skript im cgi-bin/-Verzeichnis des lokalen HTTPServers – der sich auf dem gleichen Rechner befindet wie der Browser – gespeichert ist.
Rapid Web Development mit Python
27
2 – Grundlagen der Web-Programmierung mit Python
#!/usr/bin/python
from time import*
t=localtime()
hours = t[3]
minutes = t[4]
response = """Content-Type: text/html
<html> <body>
<h2>Die aktuelle Uhrzeit </h2>
Es ist %(hours)i Uhr und %(minutes)i Minuten.
</body></html>"""
print response % vars()
#1
#2
#3
#4
Skript 2.2
Kommentar #1: In der Magic Line wird festgelegt, welches Programm diese Skriptdatei
interpretieren soll.
#2: Die Funktion localtime() aus dem Standard-Modul time liefert ein Tupel aus Integers,
das die augenblickliche Lokalzeit beschreibt. Format: (Jahr, Monat, Tag, Stunde, Minute,
Sekunde, Wochentag, Jahr im Tag, Sommerzeit)
#3: HTML-Quelltext mit Platzhaltern für die variablen Stunden- und Minutenangaben.
Ein Platzhalter beginnt mit %, danach folgt in Klammern der Namen einer Variablen und
ein Kürzel für den Datentyp (i steht für int).
#4: Mit dem Formatierungsoperator % und der Standardfunktion vars() werden die
Platzhalter in der HTML-Schablone durch die Inhalte der passenden lokalen Variablen
ersetzt.
Abbildung 2.3: Ausgabe des CGI-Skriptes timeservice.py
28
Serverseitige Web-Programmierung mit Python
2.4.4 CGI-Skripte auf einem lokalen Rechner testen
Um das Beispielskript aus Abschnitt 2.4.3 auf Ihrem lokalen Rechner zu testen, gehen Sie
folgendermaßen vor:
Speichern Sie Ihr Skript im Unterverzeichnis cgi-bin/ des Verzeichnisses ab, in dem sich
die Programmdatei des HTTP-Servers befindet.
Starten Sie Ihren HTTP-Server. Das Programm arbeitet nun im Hintergrund und beansprucht nur geringe Systemressourcen. Sie können es getrost während Ihrer gesamten
Sitzung am Rechner laufen lassen.
Öffnen Sie einen Webbrowser und tragen Sie in das Adressfeld den URI Ihres Skriptes
ein, z.B.
http://localhost:8080/cgi-bin/timeservice.py
Dabei ist localhost der voreingestellte Servername des Python-Servers aus Abschnitt
2.4.1. Der Teil :8080 bezeichnet den Port, den der HTTP-Server verwendet. Beachten Sie
folgendes Detail: Der Bezugspunkt für den Pfad hinter dem Domain-Namen im URL ist
standardmäßig das Verzeichnis, in dem sich der HTTP-Server befindet.
2.4.5 CGI-Skripte auf einem Rechner mit Internetkonnektivität
installieren
Nehmen wir an, Sie wollen ein CGI-Skript auf einem Host installieren, der mit dem
Internet in Verbindung steht – sagen wir in Ihrem Webhosting-Bereich, den Sie bei einem
kommerziellen Provider unterhalten. Inzwischen gibt es viele Hosting-Dienste, die CGISkripting mit Python anbieten. Informieren Sie sich in der Systemdokumentation oder
direkt beim technischen Support, wie die erste Zeile eines Python-CGI-Skriptes lauten
muss (der Standard bei Unix-Rechnern ist: #!/usr/bin/python) und in welches Verzeichnis
CGI-Skripte gespeichert werden müssen (häufig ist das: /srv/www/cgi-bin).
Die fertige Skriptdatei (mit korrekter erster Zeile) wird mit einem FTP-Clientprogramm
in das für CGI-Skripte vorgesehene Verzeichnis auf dem Host hochgeladen. Achten Sie
darauf, dass der ASCII-Übertragungsmodus eingestellt ist. Das ist wichtig, wenn der
Webserver auf einer anderen Plattform läuft (z.B. Unix) als der Rechner, auf dem Sie das
Skript entwickelt haben (z.B. Windows). DOS/Windows verwendet in Textdokumenten
andere Zeilenwechsel-Zeichen als Unix. Bei der Datenübertragung im ASCII-Modus
wird das Dateiformat (d.h. der Zeilenwechsel) an die Plattform des entfernten Rechners
angepasst.
Nun müssen die Zugriffsrechte der Skript-Datei richtig eingestellt werden. Der Besitzer
erhält alle Zugriffsrechte (ausführen, lesen, schreiben) und andere Benutzer des Systems
haben nur das Ausführungsrecht (chmod 711). Das heißt, alle außer Ihnen können das
Skript zwar ausführen, aber es ansonsten nicht einmal lesen. Ihr geistiges Eigentum
bleibt also geschützt. Wenn der Host eine Unix-Maschine ist (das ist der Regelfall), können Sie auf zweierlei Weise die Rechte ändern:
Rapid Web Development mit Python
29
2 – Grundlagen der Web-Programmierung mit Python
Komfortable FTP-Clients (z.B. WISE-FTP) ermöglichen die Zuweisung von Zugriffsrechten.
Alternativ können Sie sich mit einem ssh-Client (z.B. PuTTY) in den entfernten Rechner
einloggen. Gehen Sie mit dem cd-Kommando in das /cgi-bin-Verzeichnis mit den CGISkripten und definieren Sie mit dem Kommando chmod 711 * die Zugriffsrechte für alle
Dateien in diesem Verzeichnis neu.
2.4.6 Übergabe von Daten an das CGI-Skript
Unser erstes CGI-Skript produzierte zwar eine Ausgabe (Uhrzeit), konnte aber keine
Eingabedaten verarbeiten. In der Praxis werden CGI-Skripte meist von interaktiven
Webseiten aus aufgerufen, die über ein HTML-Formular Benutzereingaben abfragen.
Ein HTML-Formular definiert Komponenten zur Dateneingabe (Eingabefelder, Radiobuttons, Checkboxen etc.) und eine spezielle Schaltfläche, den Submit-Button. Wenn
man ihn anklickt, wird das CGI-Skript auf dem Server aufgerufen und ihm gleichzeitig
die Variablen und ihre Werte übergeben. Das CGI-Skript verarbeitet die erhaltenen
Daten und erzeugt den Quelltext einer neuen HTML-Seite, der in einem HTTP-Paket an
den Client zurückgesendet wird. Hier zunächst ein Beispiel für eine einfache interaktive
Webseite. Abbildung 2.4 zeigt ihre Ansicht im Browserfenster.
<html>
<body>
<form method="get" action="http://localhost/cgi-bin/login.py">
Anrede: <br/>
<input type="radio" name="anrede" value="Frau" /> Frau <br/>
<input type="radio" name="anrede" value="Herr" /> Herr <br/>
<p>Nachname: <input type="text" name="nachname" maxlength="20"
size="15" /></p>
<p><input type="submit" value="Login" /></p>
</form>
</body>
</html>
Das <form>-Tag
Die Definition des Formulars (engl. form) ist durch die HTML-Tags <form> … </form> eingerahmt. Im Start-Tag wird durch das Attribut action="http://localhost/cgi-bin/
login.py" der URI der Ressource spezifiziert, die nach einem Klick auf den Submit-Button angefragt werden soll. Mit dem Attribut method="get" wird festgelegt, dass bei diesem Request die HTTP-Methode GET verwendet werden soll. Das bedeutet, dass die
Daten des Formulars durch einen Querystring repräsentiert werden, der dem URI aus
dem action-Attribut angehängt wird. In dem Beispiel aus Abbildung 2.4 lautet der Querystring
?anrede=Frau&nachname=Meier
30
Serverseitige Web-Programmierung mit Python
Das heißt, wir haben zwei Variablen anrede bzw. nachname, die mit den Werten Frau bzw.
Meier belegt sind. Wenn man in das <form>-Tag das Attribut method="post" einträgt, werden die Variableninhalte im Body der HTTP-Nachricht übertragen. Im Adressfenster des
Browsers sieht man dann keinen Querystring. Die POST-Methode verwendet man deshalb z.B. für die Übertragung von Passwörtern1.
Eingabekomponenten
Im Innern des HTML-Formulars können durch <input>-Tags verschiedene Eingabekomponenten spezifiziert werden.
Zu Beginn des Formulars stehen zwei <input>-Tags mit dem Attribut type="radio". Sie
definieren zwei Radiobuttons, die eine Einfachauswahl realisieren. Innerhalb einer
Gruppe von Radiobuttons kann immer nur einer gedrückt sein. Zusammengehörige
Radiobuttons müssen den gleichen Namen tragen (hier: name="anrede"). Den Radiobuttons sind unterschiedliche Werte zugeordnet (hier: value="Frau" und value="Herr"). Beim
„Abschicken“ des Formulars durch Betätigen des Submit-Buttons Login wird der Wert
des ausgewählten Radiobuttons übertragen.
Mit dem <input>-Tag vom Typ "text" definieren Sie ein einzeiliges Eingabefeld. Das
Attribut size beschreibt die Länge des sichtbaren Bereichs (im obigen Beispiel 15 Zeichen), in den ein Text der Länge maxlength geschrieben werden kann. Es ist möglich, dass
maxlength größer als size ist. Dann ist unter Umständen nur ein Teil des Eingabestrings
zu sehen.
Das <input>-Tag vom Typ "submit" am Ende des Formulars spezifiziert eine Schaltfläche
(Submit-Button). Wenn man auf der Webseite diese Schaltfläche anklickt, werden die
Daten an die Ressource übertragen, die im action-Attribut des <form>-Tags angegeben ist.
Das Attribut value="Login" beschreibt den Text, der auf der Schaltfläche zu sehen ist.
Abbildung 2.4: Interaktive Webseite
1
Das bedeutet allerdings nicht, dass die Datenübertragung mit POST sicher ist.
Rapid Web Development mit Python
31
2 – Grundlagen der Web-Programmierung mit Python
Verarbeitung der Eingabedaten im CGI-Skript
Um in einem CGI-Skript Daten aus einem Request (die z.B. aus einem HTML-Formular
stammen) zu verarbeiten, verwendet man eine Instanz der Klasse FieldStorage aus dem
Modul cgi. Man importiert das Modul mit import cgi, um Namenskollisionen zu vermeiden.
import cgi
form = cgi.FieldStorage()
Das FieldStorage-Objekt kann man sich als eine Art Dictionary vorstellen: Darin sind den
Variablennamen des Formulars die Variablenwerte zugeordnet. Beachten Sie, dass die
Werte immer Strings sind. Mit Hilfe verschiedener Methoden kann man auf die Variablen zugreifen (siehe Tabelle 2.3). Die wichtigste Methode ist getvalue(). Im einfachsten
Fall hat ein Aufruf die Form
getvalue(variable).
Dabei ist variable eine Zeichenkette, die den Namen einer Variablen aus dem Formular
enthält. Man kann nun drei Fälle unterscheiden:
K Wenn in dem Request dem Variablennamen nur ein einziger Wert zugeordnet ist,
wird ein String mit genau diesem Wert zurückgegeben.
K Es ist aber möglich, dass der Request mehrere Zuordnungen der Art variable=value
enthält. Dieser Fall kann z.B. eintreten, wenn ein HTML-Formular mehrere <input>Tags mit dem gleichen Namensattribut verwendet. Dann liefert die Methode eine
Liste mit allen Werten.
K Falls der Variablenname im Formular überhaupt nicht vorkommt, wird das leere
Objekt None zurückgegeben.
Die Methode kann auch mit einem zweiten Argument aufgerufen werden. Es enthält
einen Default-Wert, der dann zurückgegeben wird, wenn der Request keine Variable mit
dem Namen variable enthält.
Abbildung 2.5: Dynamisch erzeugte Webseite
Das folgende Skript berechnet eine Antwort auf einen Request, der vom Client gesendet
wird, wenn auf der interaktiven Webseite aus dem letzten Abschnitt auf den Submit-But-
32
Serverseitige Web-Programmierung mit Python
ton geklickt wird. Speichern Sie es im Verzeichnis cgi-bin unter dem Namen login.py ab.
Das CGI-Skript liefert eine Webseite mit einer persönlichen Begrüßung (siehe Abb. 2.5).
#! /usr/bin/python
RESPONSE = """Content-type: text/html
<html>
<head><title> Login-Seite </title></head>
<body>
<h2>Guten Tag, %(lieb)s %(anrede)s %(nachname)s!</h2>
</body>
</html>"""
import cgi
form = cgi.FieldStorage()
nachname = form.getvalue("nachname", "Unbekannt")
anrede = form.getvalue("anrede", "Herr")
if anrede == "Herr":
lieb = "lieber"
else:
lieb = "liebe"
print RESPONSE % vars()
#1
#2
#3
#4
Skript 2.3
Kommentar #1: Die Variable RESPONSE ist ein langer String (drei Anführungszeichen),
der über mehrere Zeilen geht. Er ist eine Schablone mit drei Platzhaltern. Ein Platzhalter
beginnt mit %, dann folgt in Klammern ein Variablenname, dahinter ein Zeichen, das den
Datentyp spezifiziert (hier: s für String). Am Anfang steht ein HTTP-Header, der den
Typ der Nutzdaten (hier HTML-Text) angibt. Dahinter dürfen in der Zeile keine Leerzeichen stehen. Es folgt eine wirklich leere Zeile und schließlich der HTML-Text der Antwort.
#2: Es wird ein Objekt der Klasse FieldStorage aus dem Modul cgi instanziert.
#3: Der Variablen nachname wird der Wert zugewiesen, der im Querystring des Requests
dem Namen "nachname" zugeordnet ist. Es handelt sich dabei um die Zeichenkette, die
auf der interaktiven Webseite in das einzeilige Eingabefeld – definiert durch ein <input>Tag vom Typ "text" mit dem Attribut name="nachname" – eingetragen worden ist. Wenn
nichts in dieses Eingabefeld geschrieben worden ist, liefert die Methode getvalue() die
Zeichenkette "Unbekannt".
#4: In der HTML-Schablone werden die Platzhalter durch die Inhalte der referenzierten
Variablen nachname, anrede und lieb ersetzt und das Resultat an den HTTP-Server über
die Standardausgabe zurückgereicht.
Rapid Web Development mit Python
33
2 – Grundlagen der Web-Programmierung mit Python
2.4.7
Cookies
HTTP ist ein zustandsloses Protokoll. Jeder einzelne Akt der Kommunikation über das
Senden eines Request und Empfangen des Response steht für sich und ist unabhängig
von der Vergangenheit. In der Praxis gibt es aber häufig Dialoge, die über mehrere
Schritte gehen. Man denke an eine Bestellung in einem Online-Shop. Dann ist es oft sinnvoll, wenn Information über die ersten Dialogschritte gespeichert wird. Zu diesem
Zweck gibt es Cookies (engl.: Kekse). Das sind Daten, die von einem Server in Headerzeilen des HTTP-Paketes gesendet und beim Client gespeichert werden. Cookies können
ein Verfallsdatum besitzen. Ist dieses überschritten, wird das Cookie gelöscht. Fehlt das
Verfallsdatum, so wird das Cookie beim Client nicht dauerhaft gespeichert, sondern ist
nur zur Laufzeit des Clientprogramms gültig. Das heißt, wenn Sie das Browserfenster
schließen, geht auch das Cookie verloren. Technische Details des Cookie-Standards werden im RFC 2109 beschrieben (http://www.faqs.org/rfcs/rfc2109.html).
Das Python-Modul Cookie enthält die Definitionen mehrerer verschiedener Cookie-Klassen. Aus Sicherheitsgründen empfiehlt es sich, nur die Klasse SimpleCookie zu verwenden. Spielen wir nun im interaktiven Modus einige Python-Anweisungen zur CookieVerarbeitung durch. Nehmen wir an, aus irgendeinem Grund muss sich ein Web-Service
während eines Dialogs Name und Alter des Kommunikationspartners merken. Zuerst
wird ein Objekt der Klasse SimpleCookie instanziert:
>>> import Cookie
>>> c = Cookie.SimpleCookie()
SimpleCookie-Objekte werden wie Dictionaries behandelt. Man kann auf folgende Weise
neue Werte setzen:
>>> c["name"] = "Melanie"
>>> c["alter"] = 22
Letztlich werden in einem Cookie nur Strings gespeichert. Objekte anderer Typen (im
Beispiel die Zahl 22) werden automatisch in Strings umgewandelt. Mit diesen Anweisungen werden sogenannte Morsel-Objekte erzeugt (morsel: engl. „kleines Stück“) und im
SimpleCookie-Objekt den Schlüsseln "name" und "alter" zugeordnet.
>>> c["name"]
<Morsel: name='Melanie'>
>>> c["alter"]
<Morsel: alter='22'>
Jedes Morsel-Objekt repräsentiert eine Header-Zeile eines HTTP-Pakets. Mit einer printAnweisung oder der str()-Funktion wird dieser Header ausgegeben:
>>> print c["name"]
Set-Cookie: name=Melanie;
34
Serverseitige Web-Programmierung mit Python
Die textuelle (druckbare) Repräsentation des gesamten SimpleCookie-Objekts ist dann
eine Sammlung von set-Cookie-Header-Zeilen:
>>> print c
Set-Cookie: Alter = 22;
Set-Cookie: Name = Melanie;
Morsel-Objekte besitzen u.a. das Attribut value, das den Wert des Cookies enthält.
>>> c["name"].value
'Melanie'
>>> c["alter"].value
'22'
Sie erkennen, dass die Cookie-Werte immer Strings sind. Ein Morsel-Objekt ist selbst
wieder ein spezielles Dictionary. Um ein Cookie mit einem Haltbarkeitsdatum zu versehen, ordnet man dem Schlüssel "expires" eine ganze Zahl zu. Diese Zahl ist die Zeitdauer
in Sekunden, die das Cookie auf dem Client-Rechner erhalten bleiben soll.
>>> c["name"]["expires"] = 3600
>>> print c["name"]
Set-Cookie: name=Melanie; expires=Sun, 27-May-2007 11:39:58 GMT;
Sie sehen, dass die Lebensdauer in einen absoluten Zeitpunkt umgerechnet worden ist.
Zu diesem Zeitpunkt wird auf dem Clientrechner das Cookie gelöscht.
Mit der load()-Methode kann ein Cookie-Objekt mit Inhalt gefüllt werden. In einem
CGI-Skript verwendet man die load()-Methode, um ein Cookie aus der Umgebungsvariablen HTTP_COOKIE zu laden:
c.load(os.environ['HTTP_COOKIE'])
2.4.8 Beispiel: Besuchszähler
Das folgende CGI-Skript gibt im Browserfenster aus, zum wievielten Mal es aufgerufen
worden ist (siehe Abbildung 2.6). Es verwendet ein Cookie für die aktuelle Zahl der
Besuche. Bei jedem Besuch wird diese Zahl um eins erhöht. Dabei kann der Browser
ruhig zwischendurch geschlossen und wieder geöffnet werden. Das Cookie hat eine
Lebensdauer von einer Stunde. Wird nach einer Stunde Inaktivität das Skript erneut aufgerufen, beginnt die Zählung wieder bei 1.
Rapid Web Development mit Python
35
2 – Grundlagen der Web-Programmierung mit Python
Abbildung 2.6: Dynamisch erzeugte Webseite, die die Anzahl bisheriger Besuche anzeigt
#!/usr/bin/python
import Cookie, os
RESPONSE = """%(cookie)s
Content-Type: text/html
<html>
<head><title>Zähler</title></head>
<body>
Anzahl der Besuche: %(number)s
</body>
</html>"""
c = Cookie.SimpleCookie()
try:
c.load(os.environ["HTTP_COOKIE"])
c["count"] = int(c["count"].value)+1
except:
c["count"] = 1
c["count"]["expires"] = 3600
c["count"]["path"] = "/"
number = c["count"].value
cookie = str(c)
print RESPONSE % vars()
#1
#2
#3
#4
#5
#6
#7
#8
#9
Skript 2.4
Kommentar: #1: Die Konstante RESPONSE enthält in einem langen String eine Schablone
für die Antwort, die an den Client zurückgesendet wird. Der erste Platzhalter %(cookie)s
wird später bei der Formatierung (Zeile #9) durch einen HTTP-Header ersetzt, der mit
Set-Cookie: beginnt.
#2: Instanzierung eines SimpleCookie-Objekts
36
Clientseitige Programmierung
#3: Versuch, das Cookie vom Client-Rechner zu laden. Der Wert der im Cookie gespeicherten Variablen count wird um eins erhöht. Dabei muss zunächst der String in eine
ganze Zahl umgewandelt werden.
#4: Wenn kein Cookie existiert (beim ersten Besuch der Seite), wird ein neues Cookie
erzeugt, die Variable count erhält den Anfangswert '1'.
#5: Dem Cookie wird die Lebenszeit 3600 Sekunden zugewiesen.
#6: Hier wird dem Cookie ein Pfad zugewiesen. Das bedeutet, dass dieses Cookie für alle
Ressourcen im Einflussbereich des Servers gilt.
#7: Die Variable number erhält den Wert des Cookies, einen String.
#8: Aus dem SimpleCookie-Objekt wird ein HTTP-Header gewonnen, der z.B. so aussehen
kann:
Set-Cookie: count=12; expires=Sun, 27-May-2007 11:39:58 GMT; path=/;
2.4.9
CGI-Skripte debuggen
Wenn man ein fehlerhaftes CGI-Skript über einen Webbrowser aufruft, erhält man ein
leeres Fenster. Denn das Skript wird entweder gar nicht erst ausgeführt oder es bricht ab,
ehe es eine Ausgabe produzieren kann. Das erschwert die Programmentwicklung, weil
Sie keine Fehlermeldung erhalten. Zum Glück gibt es für das Debugging von CGI-Skripten das Modul cgitb. Sie brauchen in Ihr CGI-Skript nur folgende zwei Zeilen einbauen:
import cgitb
Beim Aufruf eines fehlerhaften Skriptes erscheint dann im Browserfenster eine Fehlermeldung, die Ihnen einen Hinweis über Ort und Art des Fehlers liefert. Achten Sie darauf, dass diese beiden Zeilen wieder entfernt werden, bevor Sie das Skript für die Öffentlichkeit erreichbar machen. Denn falls im Regelbetrieb tatsächlich einmal ein Problem
auftritt, verrät die Fehlermeldung von cgitb Geheimnisse Ihres Quelltextes.
2.5
Clientseitige Programmierung
Clients sind Programme, die – meist über das Internet – Kontakt mit einem Server aufnehmen und Ressourcen anfordern. Dazu gehören E-Mail-Programme, Webbrowser
oder Suchroboter. Die Standard-Bibliothek von Python enthält eine Reihe von Modulen,
die die Programmierung solcher Systeme unterstützen. Wir beschränken uns hier auf
das Modul urllib.
2.5.1
Zugriff auf Ressourcen – das Modul urllib
Das Modul urllib enthält mächtige Funktionen und Klassen zur einfachen Programmierung von HTTP-Clients. Die Funktion urlopen() liefert – wie die Standardfunktion open()
– eine Datei, aber mit dem Unterschied, dass hier an Stelle eines Pfades im lokalen Verzeichnisbaum ein URI als Argument übergeben wird und auf eine Ressource im Internet
zugegriffen werden kann.
Rapid Web Development mit Python
37
2 – Grundlagen der Web-Programmierung mit Python
Außerdem bietet das Modul urllib noch eine Reihe von Hilfsfunktionen zur Transformation von Python-Datenstrukturen in HTTP-konforme Datenformate (z.B. Querystrings).
urlopen ()
Das allgemeine Format für den Aufruf der Funktion urlopen() ist
urlopen (url[,data[proxies]])
Der erste Parameter kann ein URI sein, der mit ftp:, file: oder http: beginnt. Fehlt die
Angabe eines Schemas oder ist der Schemabezeichner file:, wird das erste Argument als
Pfad zu einer lokalen Datei interpretiert.
Der URI kann einen Querystring beinhalten, mit dem Daten an ein Serviceprogramm
(z.B. ein CGI-Skript) übergeben werden.
Als zweites, optionales Argument können Daten im Standardformat application/x-wwwform-urlencoded übermittelt werden. Damit wird von urlopen() automatisch die HTTPMethode POST angewendet. Fehlt das data-Argument, so wird in der Startzeile des
Request die Methode GET spezifiziert.
Im dritten, ebenfalls optionalen Argument proxies kann ein Dictionary übergeben werden, das für die verschiedenen Schemata (http, ftp) Proxies spezifiziert. Beispiel:
proxies={"http": "http://proxy.HTTP-example.org:8000"}
Ein Aufruf von urlopen() gibt ein File-artiges Objekt zurück. Es besitzt die Methoden
read(), readline(), readlines(), fileno(), close(), die die gleiche Funktionalität wie die
gleichlautenden File-Methoden besitzen. Zusätzlich gibt es zwei weitere Methoden
namens info() und geturl(). Die Methode info() liefert ein Objekt der Klasse mimeTool.Message. Es repräsentiert den Kopf des HTTP-Pakets. Mit einer print-Anweisung
oder str() kann man eine textuelle Darstellung erhalten, die den Originaltext des Kopfes
(außer der Statuszeile) wiedergibt.
Die Methode geturl() liefert den tatsächlichen URI der Ressource.
Lesender Zugriff auf Ressourcen
Der einfachste Anwendungsfall für urlopen() ist das Lesen einer statischen Ressource,
ohne dass zuerst Daten an den Server gesendet werden. Mit den folgenden Anweisungen laden Sie von einem FTP-Server ein Textdokument herunter:
>>> from urllib import *
>>> resource = urlopen("ftp://ftp.fernuni-hagen.de/pub/faqs/feunews.faq")
Mit der Methode info() können die Header-Informationen abgefragt werden. Bei ftp ist
das oft nur die Länge des Textes.
38
Clientseitige Programmierung
>>> print resource.info()
Content-Length: 39320
Mit der file-Methode read() werden die Daten gelesen.
>>> print ressource.read()
Frequently Asked Questions zum Newsdienst an der FernUni
- version 2.3 (12-2003) …
Die Antworten von HTTP-Servern enthalten in den Headern erheblich mehr Information. Beispiel:
>>> resource = urlopen("http://www.python.org/index.html")
>>> print resource.info()
Date: Thu, 24 May 2007 19:11:14 GMT
Server: Apache/2.0.54
Last-Modified: Thu, 24 May 2007 15:23:16 GMT
ETag: "60193-3992-dc9c4100"
Accept-Ranges: bytes
~~~~~~~~~~~~~~: ~~~~~
Connection: close
Content-Type: text/html
Hinweise zur Bedeutung der HTTP-Header finden Sie in Tabelle 2.2 auf Seite 43.
Datenübergabe an CGI-Skripte
Wenn ein Client über einen HTTP-Request ein CGI-Skript aufrufen soll, muss er häufig
gewisse Eingabedaten übermitteln. Dabei wird eine der beiden HTTP-Methoden GET
oder POST verwendet. Die Funktion urlencode() aus dem Modul urllib erleichert die Aufbereitung von Daten für die Übertragung. Als Argument wird
K ein Dictionary der Form {key1:value1, key2:value2, …} oder
K eine Liste aus Paaren der Form [(key1, value1), (key2, value2), …]
übergeben. Die Funktion berechnet daraus eine Zeichenkette, die als Querystring verwendet werden kann. Jedes Paar (key,value) wird in der Form "key=value" dargestellt
und mehrere dieser Fragmente werden durch & verbunden. Sonderzeichen werden %kodiert. Beispiel:
>>> d = {"nachname":"Krüger", "anrede":"Frau"}
>>> print urlencode(d)
nachname=Kr%FCger&anrede=Frau
Rapid Web Development mit Python
39
2 – Grundlagen der Web-Programmierung mit Python
Wir testen den Aufruf eines CGI-Skriptes mit urlopen() mit Hilfe des Beispielskriptes aus
Abschnitt 2.4.6. Achten Sie darauf, dass Ihr lokaler HTTP-Server läuft (siehe Abschnitt
2.4.1).
Bei der GET-Methode werden Daten als Querystring im URI übergeben.
>>> d = {"nachname":"Krüger", "anrede":"Frau"}
>>> data = urlencode(d)
>>> uri = "http://localhost:8080/cgi-bin/login.py" + "?" + data
>>> answer = urlopen(uri)
>>> print answer.info()
Server: SimpleHTTP/0.6 Python/2.4.3
Date: Mon, 28 May 2007 04:46:37 GMT
Content-type: text/html
>>> print answer.read()
<html>
<head><title> Login-Seite </title></head>
<body>
<h2>Guten Tag, liebe Frau Krüger!</h2>
</body>
</html>
Wenn die kodierten Daten beim Aufruf von urlopen() als zweites Argument übergeben
werden, wird für die Datenübertragung die POST-Methode verwendet:
>>> uri = "http://localhost:8080/cgi-bin/login.py"
>>> answer = urlopen(uri, data)
2.5.2 E-Mails senden mit SMTP
Das Simple Mail Transfer Protocol (SMTP) regelt das Senden von E-Mails an einen Mailserver. Das Python-Modul smtplib enthält die Klasse SMTP, mit der Sie einen E-Mail-Client
programmieren können. Ein Objekt dieser Klasse kann durch einen Aufruf im folgendem Format instanziert werden:
SMTP([host[, port]])
Beide Argumente sind optional. Das erste Argument host ist der Name des SMTP-Servers, das zweite die Portnummer. Bei einem Aufruf ohne Argument versucht der Konstruktor eine SMTP-Verbindung mit dem localhost aufzubauen. Die voreingestellte Portnummer ist 25. Das ist die Nummer, die im SMTP-Protokoll für Mailserver vorgesehen
ist. Für die Spezifikation des Ports gibt es zwei Möglichkeiten. Erstens: Der String host
besteht aus dem Hostnamen, gefolgt von einem Doppelpunkt und der Portnummer, z.B.
SMTP("localhost:25"). In diesem Fall braucht man kein zweites Argument. Oder aber Sie
geben im ersten Argument nur den Hostnamen und im zweiten Argument den Port an,
z.B. SMTP("localhost", 25).
40
Clientseitige Programmierung
Die wichtigsten Methoden der SMTP-Objekte sind in Tabelle 2.4 zusammengestellt.
Das automatische Versenden von E-Mail spielt in interaktiven Websites z.B. eine Rolle,
wenn die Authentizität eines Besuchers geprüft werden soll. Ein typischer Anwendungsfall ist die Registrierung eines Benutzers bei einem Online-Dienst. Die Person gibt ihre
Daten inklusive einer E-Mail-Adresse an. Der Server sendet dann an diese E-Mail-Adresse
eine Mail mit einem einmaligen URI, der gewisse Daten in kodierter Form enthält. Der
Empfänger der E-Mail kann dann mit dem Webbrowser diesen URI abrufen und seine
Registrierung bestätigen. Das folgende Beispiel, das die Verwendung von smtplib illustrieren soll, beschränkt sich auf eine einfachere Aufgabenstellung.
Auf einer Website kann der Besucher in ein Eingabefeld eines Formulars seine E-MailAdresse eintragen. Beim Klick auf den Submit-Button soll eine E-Mail an eben diese
Adresse versendet werden. Das System besteht aus einer statischen Webseite mit Formular (Abb. 2.7) und einem CGI-Skript, das beim Klick auf die Schaltfläche aufgerufen wird.
Es verschickt die Mail an den Absender und gibt eine HTML-Antwortseite zurück.
Abbildung 2.7: Website, die E-Mails versendet
Statische HTML-Seite
<html>
<body>
<h1> Briefdienst Online</h2>
<form action="http://localhost:8080/cgi-bin/sendmail.py">
<p>Ich brauche eine Aufmunterung.
Bitte senden Sie mir eine Mail.</p>
<p>Meine E-Mail-Adresse:
<input type="text" name="email" size="20"></p>
<p><input type="submit" value="Bestellung absenden"></p>
</form>
</body>
</html>
Skript 2.5 ist eine Implementierung des CGI-Skriptes, die davon ausgeht, dass
K das CGI-Skript,
Rapid Web Development mit Python
41
2 – Grundlagen der Web-Programmierung mit Python
K der SMTP-Server, der die Mails verschickt (sendmail) und
K der HTTP-Server, der die Anfragen bearbeitet und das Skript ausführen lässt,
sich auf demselben Rechner befinden. Dann kann der SMTP-Server als localhost angesprochen werden.
RESPONSE = """Content-type: text/html
<html><body>
<h2>Vielen Dank für den Auftrag!</h2>
Ich habe eine Mail an <i>%(toaddr)s</i> gesendet.
</body></html>"""
ERROR = """Content-type: text/html
<html><body>
<h2>Fehler</h2>
Ich konnte keine E-Mail an Sie schicken.
</body></html>"""
MESSAGE = """From: %(fromaddr)s\r
To: %(toaddr)s\r
Subject: E-Mail für Sie\r
MIME-Version:1.0\r
Content-Type: text/html\r
Content-Transfer-Encoding: quoted-printable\r
\r
Lieber Kunde, liebe Kundin!
"""
form = cgi.FieldStorage()
toaddr = form.getvalue("email", "")
#1
fromaddr = "[email protected]"
try:
client = SMTP("localhost", 25)
client.sendmail(fromaddr, toaddr, MESSAGE % vars())
client.quit()
print RESPONSE % vars()
except:
print ERROR
Skript 2.5
42
Referenz
Kommentar #1: Der Aufruf form.getvalue() liefert den Wert des Eingabefeldes email aus
dem Formular oder einen leeren String (Default), falls das form-Objekt kein Attibut email
besitzt.
2.6
Referenz
2.6.1
HTTP
Status-Code
Erklärung
200 OK
Allgemeine Erfolgsmeldung. Die angefragte Ressource ist im Body
der Nachricht.
204 No Content
Die Anfrage wurde bearbeitet, aber es wird keine Nutzinformation
zurückgesendet. Wenn der Client ein Browser ist, ändert sich der
dargestellte Inhalt nicht.
301 Moved Permanently
Die angefragte Ressource hat einen neuen permanenten URI. Dieser wird im Header Location: angegeben.
400 Bad Request
Die Syntax der Anfrage ist nicht korrekt.
403 Forbidden
Der Server versteht die Anfrage, weigert sich aber, sie zu beantworten.
404 Not Found
Die angefragte Ressource existiert nicht.
500 Server Error
Beim Server ist ein Fehler aufgetreten, z.B. durch ein fehlerhaftes
CGI-Skript.
503 Service Unavailable
Der Server ist momentan nicht erreichbar. Es handelt sich nur um
einen temporären Zustand.
Tabelle 2.1: Einige HTTP-Status-Codes nach HTTP 1.0 und HTTP 1.1
Header
Beispiel und Erklärung
Accept:
Accept: text/*
Request-Header, der angibt, welche Mime-Typen der Client akzeptiert
Accept-Ranges:
Accept-Ranges: bytes
Zeigt an, ob ein Server bereichsbezogene Anfragen bearbeitet. Wenn er es nicht
tut, wird in diesem Header none eingetragen.
Cache-Control:
Cache-Control: no-cache
Spezifiziert eine Direktive, an die sich alle Instanzen halten müssen, die am
Caching (Zwischenspeichern) von Nachrichten beteiligt sind. Dazu gehört auch
der Cache des Clients. Der obige Beispiel-Header bewirkt, dass die Nachricht gar
nicht im Cache gespeichert wird.
Tabelle 2.2: Einige Header-Definitionen nach HTTP 1.1
Rapid Web Development mit Python
43
2 – Grundlagen der Web-Programmierung mit Python
Header
Beispiel und Erklärung
Connection:
Connection: close
Zeigt an, dass die Verbindung nach der Übertragung dieser Nachricht beendet ist
Content-Encoding:
Content-Encoding: gzip
Angabe der Kodierung, mit der die Ressource in der übertragenen Repräsentation
kodiert worden ist
Content-Language:
Content-Language: de, en
Sprachen, die der Ressource zugeordnet sind
Content-Length:
Content-Length: 39320
Länge des Body der Nachricht in byte
Content-Type:
Content-Type: text/html
MIME-Typ der Daten im Body der Nachricht
Date:
Thu, 24 May 2007 19:11:14 GMT
Zeitpunkt, zu dem die Nachricht verschickt worden ist
ETag:
ETag: "60193-3992-dc9c4100"
Entity-Tag der übermittelten Repräsentation der Ressource. Das ist eine unverwechselbare Markierung, mit der der Client z.B. erkennen kann, ob er diese Version der Ressource bereits besitzt.
From:
From: [email protected]
E-Mail-Adresse des Absenders
Last-Modified:
Last-Modified: Thu, 24 May 2007 15:23:16 GMT
Datum der letzten Änderung der Ressource
Location:
http://www.w3.org/hypertext/www/newLocation.html
Neuer URI einer Ressource, die umgezogen ist. Er kann für automatisches Redirect verwendet werden
Server:
Server: Apache/2.0.54
Beschreibung des Server-Programms
User-Agent:
User-Agent: Mozilla/5.0
Client-Programm (z.B. Webbrowser), das den Request sendet
Tabelle 2.2: Einige Header-Definitionen nach HTTP 1.1 (Forts.)
2.6.2 CGI
Ein CGI-Skript wird in einem speziellen Verzeichnis – meist /cgi-bin – gespeichert. Wenn
ein URI einen Pfad zu einer Ressource in diesem speziellen Verzeichnis (oder einem
Unterverzeichnis) enthält, wird nicht die Ressource selbst zurückgegeben, sondern das
bezeichnete CGI-Skript ausgeführt, das eine HTML-Seite produziert.
Eingabedaten aus HTML-Formularen können mit einem Objekt der Klasse FieldStorage
gelesen werden. Tabelle 2.3 gibt eine Übersicht über die wichtigsten Methoden dieser
Klasse.
44
Referenz
Methode
Erklärung
getfirst(variable
[,default])
Liefert einen Variablenwert aus dem Request. Falls mehrere Werte dem
gleichen Namen variable zugeordnet sind, wird der erste Wert gewählt.
Das optionale Argument default gibt einen Wert an, der zurückgegeben
wird, falls der Request keine Angaben zu variable enthält.
getvalue(variable
[,default])
Liefert den Wert einer Variablen oder eine Liste von Werten, falls mehrere
Werte dem gleichen Namen zugewiesen wurden. Das optionale Argument
default gibt einen Wert an, der zurückgegeben wird, falls der Request
keine Angaben zu variable enthält.
has_key(variable)
Liefert den Wert True, falls der Variablenname existiert, und sonst
False
keys()
Liefert eine Liste der Variablennamen
Tabelle 2.3: Die wichtigsten Methoden von FieldStorage-Objekten
2.6.3 smtplib
Instanzierung eines Objekts der Klasse SMTP:
SMTP([host[, port]])
Vorgeingestellt sind als Host localhost und Port 25.
Methode
Erklärung
connect([host [,port]])
Stellt Verbindung mit dem Host host und der Portnummer port her.
Voreingestellt sind host="localhost" und port=25.
login(user, password)
Einloggen bei einem SMTP-Server, der eine Authentifizierung verlangt. Dies ist nicht bei allen Servern erforderlich.
sendmail(from_addr, to_
addrs, msg)
Absenden einer Mail. Die ersten beiden Argumente sind Strings mit
Absender und Empfänger, das dritte ist ein String mit dem Quelltext
der E-Mail gemäß SMTP-Protokoll (siehe Beispiel).
set_debuglevel(1)
Mit dieser Anweisung wird der Debug-Modus eingestellt. Das heißt,
der komplette Dialog mit dem Server wird in die Standardausgabe
geschrieben.
quit()
Die Verbindung wird getrennt.
Tabelle 2.4: Die wichtigsten Methoden von SMTP-Objekten
Rapid Web Development mit Python
45
2 – Grundlagen der Web-Programmierung mit Python
2.7
Literatur
Network Working Group: RFC 3986. Januar 2005. http://rfc.net/rfc3986.html
Network Working Group: RFC 1945. Hypertext Transfer Protocol – HTTP/1.0. Februar
1996. http://tools.ietf.org/html/rfc1945
Network Working Group: RFC 2616. Hypertext Transfer Protocol – HTTP/1.1. Juni 1999.
http://www.w3.org/Protocols/rfc2616/rfc2616.html
Tanenbaum, Andrew S.: Computernetzwerke. 4. Auflage. Pearson, 2003
Weigend, Michael: Python Gepackt. 3., aktualisierte Auflage. Redline, 2006
46
Herunterladen