| CGI-Programmierung
mit COMAL [CGI = Common Gateway Interface] Joachim Neuber, Dezember 2001 jneuber@foni.net |
Literatur: Die m.E. beste deutschsprachige Einführung in
CGI-Programmierung befindet sich in SELFHTML von selfhtml.teamone.de
Download des COMAL-Moduls STDOUT.EXE+comments.exe+comments.lst+cgicomal.htm
als .ZIP-Datei (59KB)
Client-Server-Programmierung am
Beispiel Browser-Webserver
-------------------------------------------------------------------------------------
Der Browser (Client) fordert beim - meist weit entfernten-
Webserver ein Dokument z.B. info.htm
an. Der Webserver sucht das angeforderte Dokument in seinem
Webverzeichnis und liefert es an den Browser zurück. Die Regeln,
nach denen sich diese Kommunikation abspielt sind im
HTTP-Protokoll (HyperText-Transfere-Protocol) definiert.
Zusätzlich gibt es die Möglichkeit, per Browser Daten an den Webserver zu schicken, den Webserver zu veranlassen, auf dem Server-Rechner ein Programm zu starten, welches die Daten verarbeitet und dann eine HTML-codierte Antwort über den Webserver zurückzuschicken. Das bekannteste Beispiel für derartige Anwendungen ist sicherlich die Suchanfrage über ein Formular bei Suchmaschinen. Die Regeln nach denen Browser, Webserver und das vom Browser auf dem Webserver aufgerufene Programm interagieren, sind in der CGI-Schnittstellendefinition festgelegt. Im Prinzip können alle auf dem Betriebssystem des Serverrechners lauffähigen Programme über CGI ausgeführt werden (Näheres regelt die Konfiguration des Webservers). Aber ein für UNIX kompiliertes C-Programm läuft natürlich nicht auf einem Webserver in einer WINDOWS-Umgebung.
Immer noch wird zur Programmierung sogenannter CGI-Programme die aus der UNIX-Welt kommende Programmiersprache PERL verwendet. Sie hat mehrere Vorteile: frei verfügbar (kostenlos!) auch für Windows-Plattformen, sehr mächtig, insbesondere mächtige STRING-Verarbeitungsroutinen, aber auch ein wenig ungewohnt. Warum daher nicht bei COMAL bleiben?
Mit COMAL CGI-Programme
schreiben
------------------------------------------------------
Auch COMAL bietet sehr mächtige
STRING-Verarbeitungsmöglichkeiten und sollte sich daher zur
CGI-Programmierung (natürlich nur unter Windows-Systemen)
besonders gut eignen. Es stellt sich daher die Frage, was eine
Programmiersprache außerdem 'bieten' muss, um sie zur
CGI-Programmierung geeignet zu machen? CGI-Programme müssen die
vom Browser übergebenen Daten entweder über die vom Webserver
gesetzte(n) Umgebungsvariable(n) oder von der Standardeingabe
(STDIN) lesen können. Auf beides hat der COMAL-Programmierer
direkten Zugriff. Das Auslesen von Umgebungsvariablen geschieht
über die Funktion environment$ des System-Packets z.B. dat$=environment$("QUERY_STRING"), die
Standardeingabe kann z.B. über die Anweisungsfolge
| anzahl#:=environment$("CONTENT_LENGTH") OPEN FILE 1, "CON:", READ dat$:= GET$(1, anzahl#) CLOSE FILE 1 |
eingelesen werden. Das einzige Problem, das COMAL einem
CGI-Programmierer beschert ist die Datenausgabe. CGI-Programme
müssen selbst eine HTML-codierte Antwort über den Webserver an
den Browser zurückschicken. Das Programm muss dazu auf die
Standardausgabe STDOUT schreiben, die der Webserver wiederum auf
sich umlenkt. Die COMAL-Programmierer haben damals aus
Geschwindigkeitsgründen die PRINT-Anweisung so implementiert,
dass sie entweder direkt in den Bildschirmspeicher schreibt, oder
die BIOS-Funktionen zum Schreiben auf den Bildschirm benutzt,
nicht jedoch die Standardausgabe des Betriebssystems
DOS-Interrupt 21 benutzt. Daher kann der Webserver die
PRINT-Ausgaben eines COMAL-Programms nicht auf sich umlenken. Die
Lösung des Problems ist jedoch einfach: Ein kleines
Assembler-Modul stdout enthält als einzige Prozedur PROC printout(a$), die den STRING a$
über den DOS-Interrupt 21 an die Standardausgabe STDOUT
schreibt. Benutzt man statt der COMAL-eigenen PRINT-Anweisung die
Prozedur printout() aus dem stdout-Modul, kann der Webserver die
Ausgaben auf sich umlenken und an den anfragenden Browser
weiterleiten.
Beipiel-Anwendung:
Kommentar-Formular
--------------------------------------------------------
Ein Beispiel zeigt die Interaktion zwischen Browser,
Webserver und CGI-Programm. Auf dem Webserver befindet sich in
seinem Dokumentenverzeichnis die Seite kommentar.htm, die ein
Formular enthält, in das der Anwender seinen Namen und seinen
Kommentar zum hoffentlich gut gemachten Webangebot einer Firma
machen kann:
Das HTML-FORMULAR:
| <!DOCTYPE HTML
PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN"> <html> <head> <title>Kommentarseite</title> </head> <body bgcolor="#E0E0E0"> <h1>Ihr Kommentar</h1> <form action="/cgi-bin/comments.exe" method="post"> <p>Name:<br><input size="40" maxlength="40" name="AnwenderName"></p> <p>Text:<br><textarea rows="5" cols="50" name="Kommentartext"></textarea></p> <p><input type="submit" value="Absenden"></p> </form> </body> </html> |
Das Formular enthält im einleitenden TAG <form> die
Anweisung action="/cgi-bin/comments.exe"
method="post", die den Webserver anweist, in
seinem Verzeichnis /cgi-bin/ das
COMAL-Programm comments.exe auszuführen. Die Daten an
das Programm werden mit der Methode POST übergeben, d.h. dass
das Programm comments.exe die Daten von der Standardeingabe lesen
muss und die Anzahl der übergebenen Bytes in der Umgebungsvariablen CONTENT_LENGTH enthalten
sind.
Das COMAL-HAUPTPROGRAMM:
| 0080 USE system 0090 USE stdout 0100 0110 e$:=CHR$(13)+CHR$(10)+"$" // Endkennung fuer printout!! 0120 0130 DIM dat$ OF 1000 0190 getdata(dat$) 0200 0210 html_begin("comments.cml") 0220 eingabedaten_verarbeiten 0230 print_("<h1>Vielen Dank fr Ihren Kommentar</h1>") 0240 print_("<br>") 0250 print_("<p>Hier zur Kontrolle Ihre Eingaben:</p>") 0260 print_("<b>"+name1$+": </b>"+wert1$+"<br>") 0270 print_("<b>"+name2$+": </b>"+wert2$+"<br>") 0280 print_("<br>") 0290 print_("<i>"+DATE$+" "+TIME$+"</i><br>") 0300 print_("<i>"+environment$("SCRIPT_NAME")+"</i><br>") 0310 0320 html_end |
Die PROZEDUREN:
| 1520 PROC html_begin(title$) 1530 // printout schreibt auf STDOUT 1540 // e$ muss wg der Endkennung $ (DOS-Konvention) angehängt werden 1550 1560 print_("content-type: text/html") 1570 print_("") 1580 print_("<html>") 1590 print_("<head>") 1600 print_("<title>"+title$+"</title>") 1610 print_("</head>") 1620 print_("<body>") 1630 ENDPROC html_begin 1640 1650 PROC html_end 1660 print_("</body>") 1670 print_("</html>") 1680 ENDPROC html_end 1690 1700 PROC getdata(REF dat$) 1710 rm$:=environment$("REQUEST_METHOD") 1720 rm$:=UPPER$(rm$) 1730 CASE rm$ OF 1740 WHEN "POST" 1750 // Daten von Standardeingabe lesen 1760 ct$:=environment$("CONTENT_LENGTH") 1770 ct#:=VAL(ct$) 1780 OPEN FILE 1,"CON:",READ 1790 dat$:=GET$(1,ct#) 1800 CLOSE FILE 1 1810 WHEN "GET" 1820 dat$:=environment$("QUERY_STRING") 1830 OTHERWISE 1840 dat$:="Es wurden keine Daten Übergeben!" 1850 ENDCASE 1860 ENDPROC getdata 1870 1880 PROC print(a$) 1890 // printout schreibt per DOS-Interrupt auf STDOUT 1900 // DOS erwartet als Endkennung das $-Zeichen 1910 // alles vor dem $-Zeichen wird ausgegeben, daher 1920 // muss an den STRING a$ CHR$(13)+CHR$(10) als Endkennung 1930 // für einen COMAL-DOS-String und das $-Zeichen angefügt werden. 1940 e$:=CHR$(13)+CHR$(10)+"$" // Endkennung fuer printout!! 1950 printout(a$+e$) 1960 ENDPROC print_ |
Einige Erläuterungen:
Die Prozedur 1700 PROC getdata(REF dat$) liest
den Inhalt der vom Webserver gesetzten Umgebungsvariablen
"REQUEST_METHOD" aus. Diese enthält die im
HTML-Dokument gesetzte Datenübergabemethode. Sämtliche
übergebenen Daten befinden sich danach im STRING dat$, der
natürlich vorher ausreichend DIMensioniert worden sein muss.
dat$ könnte z.B. folgenden Inhalt haben:
AnwenderName=Neuber&Kommentartext=Hallo+alter+Meister%0D%0AKlappt+es+nun+doch%3F%3F
Der Inhalt des Textfeldes mit dem Namen AnwenderName ist
Neuber und der Inhalt des Textfeldes mit dem Namen Kommentartext
ist Hallo...
Das COMAL-Programm muss nun diesen Datenstrom weiter bearbeiten,
d.h. in die einzelnen Bestandteile zerlegen und die Urlcodierung
der Sonderzeichen rückgängig machen. Dazu dienen die Prozeduren
split bzw. urldecode:
1880 PROC eingabedaten_verarbeiten 1890 split(dat$,"&",links$,rechts$) 1900 split(links$,"=",name1$,wert1$) 1910 split(rechts$,"=",name2$,wert2$) 1920 url_decode(wert1$) 1930 url_decode(wert2$) 1940 ENDPROC eingabedaten_verarbeiten 0550 PROC split(a$,trenn$,REF b$,REF c$) 0560 // teilt den String a$ am Trennzeichen trenn$ auf 0570 // und speichert die beiden Teile in b$ und c$ 0580 0590 pos#:=trenn$ IN a$ 0600 IF pos# THEN 0610 b$:=a$(1:pos#-1) 0620 c$:=a$(pos#+1:) 0630 ENDIF 0640 ENDPROC split 0650 0850 PROC url_decode(REF a$) 0860 // Ersetzt URL-codierte Sonderzeichen 0870 // wie z.B. %0D=CHR$(13) und +=" " 0880 LOOP 0890 asc$:=3*" " 0900 pos#:="%" IN a$ 0910 EXIT WHEN pos#=0 0920 links$:=a$(1:pos#-1) 0930 asc$:=a$(pos#:pos#+2) 0940 rechts$:=a$(pos#+3:) 0950 asc$(:1:):="$" 0960 asc$:=CHR$(VAL(asc$)) 0970 a$:=links$+asc$+rechts$ 0980 ENDLOOP 0990 1000 LOOP 1010 pos#:="+" IN a$ 1020 EXIT WHEN pos#=0 1030 a$(:pos#:):=" " 1040 ENDLOOP 1050 ENDPROC url_decode |