CGI Programmierug mit Haskell


[ Seminarthemen SS 2001] ... [ Inhaltsverzeichnis ] ... [ Fallbeispiel Damenproblem]


Die CGI-Library


Die CGI-Library

Wie im ersten Teil gezeigt, lassen sich einige Probleme in Haskell sehr viel eleganter lösen als in imperativen Programmiersprachen. Da liegt der Schluß nahe, diese Vorzüge auch bei der dynmischen Erzeugung von Webseiten zu nutzen. Ein erstes Hello World Beispiel läßt sich leicht erzeugen.
 (1) #! /usr/bin/runhugs

 (2) main :: IO()
 (3) main = putStr ("Content-type: text/html\n\n" 
         ++ "<html>"
         ++ "<head><title>HelloWorld</title></head>"
         ++ "<body><h1>Hello World</h1></body>"
         ++ "</html>")
Das Skript wird vom Kommandozeilen-Interpreter runhugs ausgeführt und seine Ausgabe als HTTP-Response an den Client zurückgeschickt.

Diese Art der Generierung von HTML Seiten ist sehr mühsam und auch fehleranfällig. Aus diesem Grund haben mehrere Autoren HTML-Bibliotheken entwickelt, die dem Programmierer Funktionen bieten mit deren Hilfe er sehr einfach valide Dokumente erzeugen kann. Die im folgenden näher betrachtete cgi-Bibliothek von Erik Meijer bietet dem Benutzer darüber hinaus eine Schnittstelle mit deren Hilfe er von der gesamten Low-Level Kommunikation via HTTP abstrahieren kann. Dies hat den Vorteil, daß sehr viel mehr Zeit in die Lösung des eigentlichen Problems gesteckt werden kann.


Aufbau und Arbeitsweise

Um die vielen Details des HTTP für den Entwickler der Skripte transparent zu halten, besteht die Bibliothek aus zwei Komponenten. Einem Wrapper und einem Worker. Die Aufgaben, die bei jedem cgi-Skript gleich sind, werden vom Wrapper übernommen. Die eigentlich Programmlogik wird im Worker gekapselt.

Durch diesen Ansatz ist es möglich, kürzere und besser wartbare Programme zu schreiben.

Der Ablauf im Wrapper stellt sich in etwa wie folgt dar.

        wrapper :: (Request -> IO Response) -> IO ()
 (1)  	wrapper = \worker ->
           do { 
 (2)         request <- getRequest
 (3)         ; response <- worker request
 (4)         ; putResponse response
           }
           'catch'
 (5)         (\ioerror -> do {putResponse(status ioerror)})
Die Definition des Wrapper ist ein Beispiel für die Nutzung von Higher Order Functions. Der Funktion wrapper wird eine Funktion mit der Signatur übergeben, die in (1) als worker benannt und in (3) aufgerufen wird. Vor dem Aufruf des Workers wird der low-level Request in einen abstrakten Request vom Typ Request umgeformt. Dieser Request wird dem Worker als Parameter mitgegeben. Das Ergebnis des Workers (ein Wert vom Typ Response) wird in (4) in einen low-level Response umgewandelt und dem Client zurückgeschickt. Im Falle eines Fehlers wird der Fehlercode automatisch an den Client übermittelt.

Ich verzichte in dieser Ausarbeitung bewußt auf die Details zur Implementierung dieser Umformungen, da sie - wie bereits mehrfach erwähnt - für die Benutzung der Bibliothek keine Rolle spielen. Der interessierte Leser sei auf die exzellente Dokumentation von Meijer verwiesen.

 


Modellieren von HTML

Ein Dokument, das in der Hypertext Markup Language (HTML) beschrieben ist besteht aus einer Reihe von in einander geschachtelten Elementen wie beispielsweise Überschriften, Tabellen, Aufzählungen, Verknüpfungen und Bilder. In den meisten Fällen werden diese Elemente durch ein öffendes und eine schließendes Tag beschrieben. Häufig lassen sich die Elemente durch Einfügen optionaler Attribute der Form parametrisieren. Die hier vorgestellte Bibliothek definiert einen Datentyp HTML als:
 data HTML = Text String | Element Tag [(Name, Value)] [HTML]

Die Typen Tag, Name und Value sind Synonyme für den Datentyp String (vergleiche Abschnitt 3.2). Somit ist ein Wert des Typs HTML entweder einfacher Text oder ein Element. Ein Element besteht aus einem Tagnamen, beliebig vielen Attributen und beliebig vielen weiteren Elementen. Um diese Elemente nicht per Hand erzeugen zu müssen stehen selbstverständlich Funktionen bereit, die diese Aufgabe übernehmen. Folgenden Übersicht zeigt die wichtigsten Funktionen . Unter Zuhilfenahme dieser Funktionen lassen sich sehr einfach selbst komplexe Seiten erzeugen.

 page :: String -> [(Name,Value)] -> [HTML] -> HTML
 h1 :: String -> HTML
 h :: Int ->String -> HTML
 p :: [HTML] -> HTML

 hr :: HTML
 href :: URL -> [HTML] -> HTML
 img :: String -> URL -> HTML

 ol :: [[HTML]] -> HTML
 ul :: [[HTML]] -> HTML
 dl :: [(String,[HTML])] -> HTML

 table :: String -> [String] -> [[[HTML]]] -> HTML 

Eine einfache Seite

Kommen wir noch einmal auf das Hello World Beispiel zurück. Der in Abschnitt 4 recht umständlich erzeugte Response kann mit Hilfe der von Erik Meijer erstellten cgi-Bibliothek wie folgt geschrieben werden.
 import CGI
				
 hello :: HTML
 hello = page "Hello World" [("bgcolor", "#000000")] [h1 "Hello World"]
				
 main :: IO ()
 main = wrapper (\env -> do return (Content(hello)))
Bei diesem zugegebenermaßen sehr einfachen Beispiel ist der Schreibaufwand nicht viel geringer als bei der ersten Version, aber das Programm gewinnt stark an Lesbarkeit und Wartbarkeit. Je umfangreicher und komplexer die zu erstellende Seite ist, desto höher sind diese Vorteile einzuschätzen.

Lesen von Umgebungsvariablen

Wie bereits erwähnt ist es für den Worker (das eigentliche Skript) vollständig transparent, wie die Umgebungsvariablen gelesen werden. Dementsprechend einfach ist es für den Worker, diese zu extrahieren. Beim Aufruf des Wrappers werden die Umgebungsvariablen und die dem cgi-Skript übergebenen Variablen direkt als Liste von Schlüssel-Wert Paaren übergeben. Das nachstehende Beispiel listet alle übergebenen Umgebungsvariablen auf eine HTML Seite auf. Die Liste enthält auch alle die durch die URL zusätzlich übergebenen Parameter. Selbstverständlich läßt sich mit Hilfe der Funktion auch indiziert auf eine spezielle Variable zugreifen.
 seite :: [(Name, Value)] -> HTML
 seite env = page "Umgebungsvariablen" []
    [
      h1 "Umgebungsvariablen",
      dl (map (\(dt,dd) -> (dt, [prose dd])) env)
    ]
	
 main :: IO ()
 main = wrapper(\env -> do return (Content(seite env)))

Das vollständige Skript befindet sich zum Download im Abschnitt [ Download ].


[ Seminarthemen SS 2001] ... [ Inhaltsverzeichnis ] ... [ Fallbeispiel Damenproblem]