Python ja CGI-ohjelmointi
CGI eli Common Gateway Interface on yksinkertaisin rajapinta WWW-sovelluksien tekemiseen
HTML-dokumentti Python-kielellä ja CGI-rajapinnan kautta tuotettuna:
#!/opt/rh/rh-python38/root/usr/bin/python3.8
# -*- coding: utf-8 -*-
import cgitb
cgitb.enable()
print("""Content-type: text/html; charset=UTF-8
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Malli</title>
</head>
<body>
<h1>Tulostetaan Pythonilla:</h1>
<p>
""")
print("Hello World! Osaatkönä skändejä?")
print("""
</p>
</body>
</html>
""")
- #!/opt/rh/rh-python38/root/usr/bin/python3.8 kertoo mistä www-palvelimen pitää etsiä käytettävää komentotulkkia eli tässä tapauksessa pythonia. Yleensä
#!/usr/bin/pythontai#!/usr/bin/python3 - # -*- coding: utf-8 -*- varmistaa, että lähdekoodi käsitellään UTF-8-merkistönä eikä vahingossakaan esim. ISO-8859-1-muotoisena
- print("""Content-type: text/html; charset=UTF-8 Tulostetaan useampi rivi tekstiä jolloin pythonissa käytetään """-merkintää osoittamaan
tulostuksen alku.
Ensimmäiselle riville pitää tulostaa www-selaimen tarvitsema tieto dokumentin mediatyypistä HTTP-otsakkeena (text/html). HTTP-otsakkeen yhteydessä voidaan myös asettaa käytetty merkistö.
Muitakin HTTP-protokollan otsakkeita voitaisiin tulostaa tässä vaiheessa. Varsinainen HTML-dokumentti alkaa ensimmäisen tyhjän rivin jälkeen.
- """ Monen rivin tuloste loppuu
- print "Hello World!" Tavallinen yhden rivin tulostus
Virheilmoitukset ja asetukset
Pythonilla tehtyjen CGI-ohjelmien debuggaamista helpottaa cgitb-moduulin käyttöönotto:
import cgitb
cgitb.enable()
cgitb aiheuttaa virheraporttien näyttämisen www-selaimessa tai niiden tallentamisen tekstitiedostoon.
Oletuksena cgitb-kirjasto tulostaa virheilmoitukset HTML-muodossa. Jos haluat tekstimuotoisen tulostuksen niin käytä kutsua:
cgitb.enable(format="text")
CGI-ohjelmat users.jyu.fi-palvelimella
Viimeisimmät tiedot löytyvät aina digipalvelujen ohjeesta: CGI/SSI-tekniikoiden käyttäminen www-palveluissa (users.jyu.fi/groups.jyu.fi)
- CGI-ohjelmien on oltava W:\cgi-bin\-kansiossa tai sen alikansiossa. Mikään muu kansio ei kelpaa
- CGI-ohjelmien tiedostopääte on oltava .cgi
- Hakemiston (jossa ajettava tiedosto on) omistajan/ryhmän pitää olla samat kuin itse käyttäjän (uid/gid). Nämä tiedot saa selville id-komennolla.
- Suoritettavan tiedoston omistajan ja ryhmän pitää olla samat kuin käyttäjän, jona tiedosto suoritetaan.
- Suoritettavaan tiedostoon tai hakemistoon ei saa olla kirjoitusoikeutta muilla kuin itse käyttäjällä.
- Harvinaisissa tapauksessa SELinux voi aiheuttaa ongelmia. Toimi silloin digipalvelujen ohjeen mukaan.
Python ja merkistöt
- Sinun on aina tiedettävä mikä merkistö on merkkijonoissa käytössä. Tämä koskee kaikkia ohjelmointikieliä
- Kirjoita ohjelmakoodi ja sen merkkijonot UTF-8-merkistössä
# -*- coding: utf-8 -*- - html-dokumentin merkistön on myös oltava UTF-8 (content-type, mahdollinen xml-deklaraatio. mahdollinen meta-elementti, formin accept-charset)
HTTP-otsakkeessa oikea merkistö:
Content-type: text/html; charset=UTF-8Pelkässä tekstimuodossa:
Content-type: text/plain; charset=UTF-8Edelläolevan pitäisi riittää mutta jos merkistö on mainittu jossain muuallakin niin myös siellä UTF-8:
<!doctype html> <html> <head> <meta charset="UTF-8" /> <title>Malli</title> </head> <body> <form action="" method="post" accept-encoding="UTF-8"> </form> </body> </html> - Jos joudut käyttämään muuta merkistöä kuin UTF-8, muista encode
Querystring
- Sivun osoitteeseen voidaan lisätä parametreja seuraavalla tavalla:
http://osoite.example?parametri=arvo¶metri2=arvo1¶metri2=arvo2 http://osoite.example?nimi=M%C3%A4lli%20Henkil%C3%B6&arvosana=5&arvosana=3&arvosana=2&kurssi=Helppo+kurssi&kurssi=Vaikea+kurssi&kurssi=%C3%84ll%C3%B6+kurssiOsoitteen perään lisätään ?-merkki. ?-merkin jälkeen luetellaan avaimia ja arvoja &-merkillä eroteltuina
Osoitteessa esiintyvät erikoismerkit on koodattava Percent-encoding-tavalla. Pythonissa tämän voi tehdä urllib.quote_plus-funktiolla.
Jos query_stringin sisältävän osoitteen sijoittaa html-dokumenttiin on muistettava koodata myös &-merkit cgi.escape-funktiolla.
- Querystringissä annettuihin parametreihin pääsee käsiksi.
Pythonin cgi-kirjaston avulla. WWW-sivuilla
olevien lomakkeiden lähettämät tiedot käsitellään samalla tavalla. Tähän palataan seuraavien viikkojen tehtävissä.
import cgi fields = cgi.FieldStorage() # alustaa lomake-olion #pyytää ensimmäisen url-nimisen lomakekentän arvon. Jos sitä ei ole annettu käyttää #oletusmerkkijonoa "1". #Tiedot ovat aina merkkijonoja paitsi oletusarvoksi voi antaa myös muita tyyppejä nimi = fields.getfirst("nimi", "Tuntematon") # Jos samalla parametrilla on useampia arvoja niin ne voidaan pyytää listana # Lista on tyhjä, jos pyydettyä parametria ei löydy arvosanat = fields.getlist("lkm")
Flask ja users.jyu.fi
Lopullinen sovellus pitää yleensä saada toimimaan muualla kuin omalla koneella ja toiminnallisuudessa voi olla eroja esim. tiedostopolut voivat olla erilaisia. Debuggaus on kuitenkin huomattavasti helpompaa omalla koneella. Jos on W:-asema verkkolevynä niin voi suoraan sieltä ajaa sovellustaan oman koneen Flaskilla ja testailla samaan aikaan myös users.jyu.fin kautta.
Flaskia aloitellessa muistakaa users.jyu.fi:ssä kaikki temput mitä vaadittiin, että cgi-ohjelma lähtee toimimaan:
- oltava cgi-bin-kansiossa
- ryhmän oltava users
- suoritusoikeus ja lukuoikeus ryhmällä ja muilla
- cgi-ohjelmaan ei saa olla kirjoitusoikeutta ryhmällä ja muilla
- Myöskään samaan kansioon ei saa olla kirjoitusoikeutta ryhmällä ja muilla
- Flask hoitaa itse http-otsakkeet
- Mitään ei saa printata flask-funktiosta vaan vain ja ainoastaan voidaan palauttaa return-lauseessa yksi merkkijono
users.jyu.fi
Kts. pääteohjaus 1
Flaskin debuggaus users.jyu.fi:ssä
Joskus täytyy itse logata virheet. Seuraava tekee lokitiedoston kansioon '../hidden/'. Korjaa polku oikeaksi:
import logging
logging.basicConfig(filename=os.path.abspath('../hidden/flask.log'),level=logging.DEBUG)
logging.debug("Tänne mitä haluaa lokiin kirjoittaa")
Älä kirjoita lokia cgi-bin-kansioon tai sen alikansioihin!. Tämä ei toimi.
Jotta lokista olisi iloa on käytettävä try..exceptiä. Nappaa exceptillä kiinni mahdolliset virheet ja dumppaa virheilmoitus lokiin:
try:
# sqlite haluaa absoluuttisen polun
con = sqlite3.connect(os.path.abspath('../hidden/resepti'))
except Exception as e:
logging.debug("Kanta ei aukea")
# sqliten antama virheilmoitus:
logging.debug(str(e))
Lokin sisältöä vo seurailla helpoiten ottamalla pääteyhteyden halavaan ja käyttämällä tail-komentoa:
tail -f flask.log
Tämä kyttää koko ajan tiedoston sisältöä ja näyttää heti sinne tulevat uudet rivit.
Järkevintä on debugata omalla koneella olevalla Flaskilla, koska siellä näkyy suoraan palvelimen virheloki.
Esimerkkejä
Python ja CGI-ohjelmointiluento (youtube, 2016)
Vanha Python ja CGI-ohjelmointiluento (youtube, 2014)
JSON, omat luokat, lomakkeen korvaaminen linkillä (Lähdekoodi, Template )
ostoskori.cgi (lähdekoodi, template)
Käyttäjien kommentit