Python og CGI
Common Gateway Interface
Utgangspunktet er en situasjon der jeg som Windowsbruker som skal lage CGI-script på en Linux server. Det er et par ting som er verdt å merke seg. Det gjelder formen på selve scriptet, debugging og manipulasjon av rettigheter til de filene vi lage på tjeneren.
Jeg forutsetter at jeg kan nå filene på tjeneren på to måter.
- Jeg kan se og manipulere kataloger og filer via File Explorer. Det vil si at Linux serveren er mappet opp mot min maskins filsystem via Samba, og at mitt område på serveren er blitt tildelt en bokstav som identifikator.
- Jeg kan åpne et "Linux-vindu" mot serveren ved hjelp av
SecureShell (eller et annet program).
Oppkopling
Når jeg starter SecureShell får jeg opp et vindu og jeg velger Quick Connect. Dialogboksen som skal fylles inn for å få kontakt ser slik ut:
Host Name er den serveren du vil kjøre mot, f.eks. brage.hiof.no
User Name er det brukernavnet du har på serveren, f.eks. borres
Som Authentication Method velger du Password. Når dette er klart trykker du
Connect og svarer med riktig passord når du blir spurt.
Med mitt utgangspunkt trenger jeg å være koplet opp mot serveren via Secure Shell av to årsaker. For det første vil jeg kontrollere og sette riktige rettighter til de filene jeg skal arbeide med. For det andre kan jeg bruke det vinduet jeg har åpnet mot serveren til debugging.
.Rettigheter
Sett rettighetene til 755
chmod 755 minfil.py
Det vil si at vi gir rettighetene: rwx r-x r-x. Alle kan lese og kjøre programmer, mens bare du kan skrive.
Dersom vi gir færre rettigheter får vi problemer med å få til den ønskede funksjonaliteten, og dersom vi gir flere rettigheter er det mulig at serveren protesterer. Grunnen til dette er at det kan være lagt inn sikkerhetskontroll så vi ikke åpner for at hvem som helst kan endre våre filer. Scriptet vårt nekter å kjøre, uten at vi får noen instruktiv feilmelding.
De som bruker UltraEdit under Windows kan støte på et lite problem. Dersom du har satt opp UE til å lage back-up filer, kan du oppleve at rettighetene ødelegges når du sparer fila. Du kan unngå dette ved enten å skru av back-up eller ved å legge til en linje i fila: winnt\uedit32.ini:
DontUseRename=1.
Debugging
Siden scriptet vårt kjører på serveren, har vi ikke slik kontroll over hvor feilmeldinger havner som når vi kjører på vår egen maskin. Jeg pleier derfor å teste ut så mye som mulig av scriptet på min maskin før jeg laster det over på serveren.
Det er to måter å debugge serverskript i Python:
-
Lese feilrapport fra serveren. Når serveren har problemer med å utføre en HTTP-forespørsel (som åkjøre et skript) lages det en feilrapport. Denne feilrapporten kan inneholde tekst som er generert fra serveren selv eller fra skripttolkningen i Python. Vi kan åpne et vindu i SSH og be om å få se denne rapporteringen fortløpende.Kommandoen som gir oss denne rapporteringen avhenger av hvordan serveren er satt opp. systemansvarlige skal kunne svare på det. Eksempler som angir hvordan en slik kommando under Linux kan være:
tail -f /var/log/httpd/error_log tail -f /var/log/apache/error.log tail -f /var/log/apache2/error.log tail -f /var/log/httpd/error_log | grep ml150
Den siste avgenser rapporteringen til rapporter som gjelder bruker ml150 -
Skru på "CGI Trace Back" i Python. Skriv følgende linje i starten av skriptet:
import cgitb; cgitb.enable()Merk at det er ikke alle feil som traces. Alle feil som skyldes syntaksfeil som oppdages før skriptet startes rapporteres som server-feil, dvs. server har ikke klart å starte skriptet.Skru av Trace Back, fjern linja som oppgitt over, nå skriptet er ferdig testet og skal i produksjon.
CR-LF
Tekstfiler på Unix/Linux lagres med bare linjeskift(LF), mens Windows normalt lagrer filene med "vognretur" og linjeskift (CRLF). Normalt, f.eks. i nettlesere, skaper ikke dette noe problem siden de fleste programmer justerer for denne forskjellen. Shellet på Linux som skal tolke vår fil før den starter Python liker ikke CRLF i det hele tatt. Det vil si at vi må passe på at de filene vi legger ut som CGI-script på en Linux-server følger Linux standarden og bare har LF. Hvis vi editerer med Linuxverktøy på Linux er ikke dette noe problem, men hvis vi editerer på Windows med Windowsverktøy må vi ta våre forholdsregler. Vi må ha en editor som legger fra seg filene slik Linux liker dem samtidig som de må vise dem fram på en fornuftig måte mens vi editerer.
Det er mange måter å ordne dette på. Jeg bruker Ultraedit. Ultraedit kan gjøre to nyttige ting i denne sammenhengen.
- transformere filer mellom DOS og Unix. Se menyen File/Conversions
- kan settes opp slik at den alltid skriver filer tilbake i den formen de ble åpnet. Se menyen Advanced/Configuration og tab General
Eksempel 1
Et minimumseksempel på et CGI-script kan være slik:
#! /usr/bin/python2 print 'Content-type: text/plain\n' print 'hello'
Den første linja angir at denne file skal leses og kjøres av python2 som er plassert slik stien angir. Dette er en konvensjon i Unix/Linux. Filer som ligger på cgi-bin er kandidater for å eksekveres, og linux må finne ut hvem som skal gjøre det. Fileextensions spiller ikke samme rolle i Linux som i Windows. Den andre linja er obligatorisk i alle cgi-script og angir hva som kommer for det programmet som mottar resultatet. Dersom vi skal skrive HTML-kode bør vi skrive:
print 'Content-type: text/html\n'
Nettlesere har variabel følsomhet for dette. Noen tolker html selv om vi angir text/plain.
Den tredje linja er din melding til verden. Vi ser at det er ikke noen koplett HML-fil, men siden nettleserne er så tilgivende vil de fleste presentere resultatet.
Gi denne fila et navn, f.eks.: mincgi.py og legg den ut på ditt serverområde under katalog cgi-bin. Lag denne katalogen hvis du ikke har den. Sett rettigheter som vist ovenfor. Test den ved å skrive URL'en i adressefeltet i en nettleser. Adressen kan være slik for bruker ml150:
http://www.ia-stud.hiof.no/~ml150/cgi-bin/mincgi.py
eller den kan være slik:
http://frigg.hiof.no/ml150/cgi-bin/mincgi.py.
Sjekk med systemansvarlig i de omgivelsene du jobber i.
.Eksempel 2
I dette eksempelet kopler vi et script opp mot en HTML-side der vi skal fylle inn noen data og sende dem til scriptet. Vi lager en minimalistisk HTML-fil for enkelhetens skyld, merk at adressen til pythonscriptet er satt opp for bruker ml150 på serveren ml:
<html> <head> </head> <body> <form action="http://www.ia-stud.hiof.no/~ml150/cgi-bin/min2cgi.py" method="POST"> <p>Navn: <textarea name="navn" value="1" cols="20" rows="1"> </textarea></p> <p>Adresse: <textarea name="adresse" value="2" cols="20" rows="1"> </textarea></p> <input type="submit" value="Send inn"> </form> </html>
Vi lager mottagerscriptet slik:
#! /usr/bin/python2 import cgi form=cgi.FieldStorage() print 'Content-type: text/plain\n' print form navn='' adresse='' try: navn=form['navn'].value except: navn='ukjent' try: adresse=form['adresse'].value except: adresse='ukjent' print 'Dette er det jeg fant:\n' print 'Navn: '+navn+'\n' print 'Adresse: '+adresse+'\n'
Vi ser at her har vi importert modulen cgi. Det gjøre det mulig for oss å få input dataene, parametrene, til scriptet ordnet i et FieldStorage som i all hovedsak oppfører seg som et Python dictionary (form=cgi.FieldStorage()). Det første vi gjør er å skrive denne rett ut. Etterpå sjekker vi hva vi faktisk har fått inn og lager en litt mer menneskevennlig utskrift.
.Eksempel 3
Dette eksempelet gjør det samme som eksempel 2, bortsett fra at det produserer en full HTML-fil som returneres. Koden nedenfor er derfor et alternativ til scriptet over.
#! /usr/bin/python2 import cgi, string print 'Content-type: text/html\n' # preparing the content in body-element form=cgi.FieldStorage() navn='' adresse='' try: navn=form['navn'].value except: navn='ukjent' try: adresse=form['adresse'].value except: adresse='ukjent' # preparing the complete HTML-string S= """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <META http-equiv="Content-Type" content="text/html; charset=iso-8859-1\"> <title>navn og adresse</title> </head> <body> <h2>Dette er det jeg fant</h2> <p>Navn: %s</p> <p>Adresse: %s</p> </body> </html> """ S=S%(navn,adresse) print S
Eksempel 4
Dette eksempelet rapporterer alle omgivelsen til Python på tjeneren. Det er altså denne informasjonen vi kan benytte oss av når vi skriver CGI-skript, i tillegg til parametere og cookies.
#! /usr/bin/python2.4 print "Content-type: text/html" print print "<pre>" import os, sys from cgi import escape print "<strong>Python %s</strong>" % sys.version keys = os.environ.keys() keys.sort() for k in keys: print "%s\t%s" % (escape(k), escape(os.environ[k])) print "</pre>"