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&auml;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&uuml;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