CGI-ohjelmointi, HTTP-protokolla
- Lomake - form
- Lomakkeen käsittely
- HTTP (HyperText Transfer Protocol)
- Pythonin cgi-kirjasto
- Templatet ja Jinja
- Python, merkistöt ja erikoismerkit webissä
Jatketaan Pythonin, CGI-ohjelmoinnin ja HTTP-protokollan parissa.
Uusin Python ja CGI-ohjelmointiluento (youtube)
Vanha Python ja CGI-ohjelmointiluento (youtube)
Luentoesimerkit: jinja.cgi-esimerkki (Lähdekoodi, Jinja template)
Toinen jinja-esimerkki (Lähdekoodi, Pohjatemplate, Toinen template)
JSON, omat luokat, lomakkeen korvaaminen linkillä (Lähdekoodi, Template )
ostoskori.cgi (lähdekoodi, template)
Lomake - form
form-elementti määrittelee lomakkeen varsinaisen alkamis- ja loppumiskohdan. form-elementin sisään sijoitetaan kaikki varsinaiset lomake-elementit (tekstikentät, valintaruudut, yms.), jotka tulevat näkyviin lomakkeelle. form-elementin attribuuteilla eli ominaisuuksilla voidaan määritellä tarkemmin lomakkeen käyttäytymistä. Seuraavassa lyhyt esimerkki form-elementin käytöstä:
<!DOCTYPE html> <html> <head> <title>Esimerkkilomake</title> </head> <body> <h1>Esimerkkilomake</h1> <form action="http://palvelin/teepase.cgi" method="post"> .. lomake-elementit tulevat tänne.. </form> </body> </html>
action-attribuutti
action-attribuutti kertoo lomakkeelle osoitteen, jossa lomakkeen käsittelevä CGI-ohjelma sijaitsee. Seuraavassa muutama esimerkki action-attribuutin käytöstä. Ensimmäisessä esimerkissä on perinteinen absoluuttinen hakemistoviittaus ohjelmaan, joka käsittelee lomakkeelle syötetyn datan.
<form action="http://palvelin/teepase.cgi" method="post">
Edellisen esimerkin mukaisesti lähetettynä data tulee koodattuna application/x-www-form-urlencoded-muodossa.
<form action="http://palvelin/teepase.cgi" method="post" enctype="multipart/form-data">
Antamalla enctype-attribuutin arvoksi multipart/form-data voidaan lomakkeeseen lisätä myös tiedostoja
method-attribuutti
method-attribuutti kertoo kuinka lomakkeen sisältämä data toimitetaan datan käsittelevälle ohjelmalle. Attribuutin mahdolliset arvot ovat post ja get.
method-attribuutti arvolla get saa aikaan lomakkeella olevien tietojen siirtämisen URL:n yhteydessä. Seuraavassa on esitelty muoto, jossa tiedot siirretään lomakkeen käsittelevälle ohjelmalle. Lomakkeelta siirrettävät tiedot ovat nähtävillä selaimen osoitepalkissa (location, address)
http://palvelin/teepase.cgi?Nimi=Etunimi+Sukunimi&email=Sahk%F6postiosoite&kommentti=t%E4h%E4n+kommentti&laheta=L%E4het%E4+kommenttisi
URL:n mukana tulevien tietojen muoto näyttää ensi silmäyksellä sekavalle. Kuitenkin sieltä on helposti erotettavissa lomakkeen tiedot. Lomakkeen käsittelevän ohjelman osoite ja lomakkeen tiedot on erotettu kysymysmerkillä (?). Eri kenttien tiedot on erotettu &-merkillä. Lomakekentän nimi ja arvo on erotettu yhtä suuri kuin -merkillä (=). Lomakkeessa olevat skandit (ä,ö,å) ja erikoismerkit on koodattu heksadesimaaliluvuiksi ja välilyöntien paikalla on plus-merkki (+) tai %20.
POST-metodia käytettäessä tiedot eivät siirry URL:n mukana, vaan vastaanottavan ohjelman on luettava ne STDIN-muuttujasta. POST-arvoa kannattaa käyttää erityisesti suuren tietomäärän siirtämiseksi lomakkeelta käsittelevälle ohjelmalle. Lomakkeelta siirtyvät tiedot eivät myöskään ole käyttäjän nähtävissä käytettäessä post-metodia. POST-metodia käytetään kun lomake aiheuttaa lisäyksiä, muutoksia tai poistoja
<!DOCTYPE html"> <html> <head> <title>Esimerkkilomake</title> </head> <body> <h1>Esimerkkilomake</h1> <form action="http://palvelin/teepase.cgi" method="post"> <p> <label for="name">Nimi: </label> <input id="name" type="text" name="Nimi" value="Etunimi Sukunimi" /> </p> <p> <label for="email">Email: </label> <input id="email" type="text" name="email" value="Sahköpostiosoite" /> </p> <p> <label for="kommentti">Kommentti: </label> <input id="kommentti" type="text" name="kommentti" value="tähän kommentti" /> </p> <p> <input type="submit" name="laheta" value="Lähetä kommenttisi" /> </p> </form> </body> </html>
Lomake-elementit käsitellään tarkemmin ITKP1011-kurssilla.
Lomakkeen käsittely
- Selainohjelma pyytää palvelimelta lomakesivun HTTP-protokollan mukaisilla otsikkotiedoilla.
- Palvelin vastaa selaimelle HTTP-protokollan mukaisilla otsikkotiedoilla, jonka jälkeen tarjoaa WWW-sivun datan eli HTML-koodia.
- Käyttäjä täyttää ja hyväksyy lomakkeen. Selain lähettää lomakkeen tiedot WWW-palvelimella suoritettavalle ohjelmalle.
- Lomakkeella oleva data käsitellään ohjelman tekijän haluamalla tavalla.
- Ohjelma palauttaa käyttäjälle virheilmoituksen tai ilmoituksen onnistumisesta, esim. lomakkeen korjauspyynnöillä tai tulossivun.
Lomakkeen käsittely
- Lomaketiedot tulevat GET- tai POST-metodilla.
- GET = lomaketiedot lisätään osoitteen loppuun. Vrt. Googlella tehty haku, jonka voit bookmarkata ja lomakkeen tiedot näkyvät sivun osoitteessa.
- POST = tiedot lähetettään HTTP-pyynnön runko-osassa.
- Käytä GET-metodia tilaa muuttamattomissa pyynnössä, joista voidaan tehdä kirjanmerkki, kuten esim. haut. Lisäykset, poistot, päivitykset ja paljon dataa lähettävät lomakkeet tehdään aina POST-metodilla.
- Tiedot käsitellään WWW-palvelimessa suoritettavalla ohjelmalla
- Ohjelma saa tarvittavat lomaketiedot ja palvelintiedot ympäristömuuttujista. Kokeile
malliohjelmaa, joka tulostaa
ympäristömuuttujien arvot. Kts. Malliohjelman lähdekoodi
CONTEXT_DOCUMENT_ROOT /home/tjlahton/html SERVER_SOFTWARE Apache CONTEXT_PREFIX /~tjlahton SERVER_SIGNATURE <address>Apache Server at users.jyu.fi Port 80</address> REQUEST_METHOD GET SERVER_PROTOCOL HTTP/1.1 QUERY_STRING foo=bar PATH /usr/local/bin:/usr/bin:/bin HTTP_USER_AGENT Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0 HTTP_CONNECTION keep-alive SERVER_NAME users.jyu.fi REMOTE_PORT 32051 SERVER_PORT 80 SERVER_ADDR 130.234.10.207 DOCUMENT_ROOT /var/www/virtual.hosts/users.jyu.fi SCRIPT_FILENAME /home/tjlahton/html/cgi-bin/env.cgi SERVER_ADMIN webmaster@cc.jyu.fi HTTP_DNT 1 HTTP_HOST users.jyu.fi SCRIPT_NAME /~tjlahton/cgi-bin/env.cgi REQUEST_URI /~tjlahton/cgi-bin/env.cgi?foo=bar HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 GATEWAY_INTERFACE CGI/1.1 REMOTE_ADDR 130.234.160.35 HTTP_ACCEPT_LANGUAGE en,fi;q=0.7,chrome://global/locale/intl.properties;q=0.3 REQUEST_SCHEME http HTTP_ACCEPT_ENCODING gzip, deflate UNIQUE_ID VrOUbXOFmxsC2sW0mvTPTgAAAAI
- WWW-Palvelimen ja suoritettavan ohjelman välinen rajapinta on määritelty CGI (Common Gateway Interface)-spesifikaatiossa.
- Huomaa, että HTTP on oletuksena tilaton. Emme tiedä edellisistä pyynnöistä tai lomakkeiden käsittelyistä mitään.
HTTP (HyperText Transfer Protocol)
HTTP on protokolla, jonka avulla selain ja WWW-palvelin keskustelevat.
Firefox-selaimen ja WWW-palvelimen välistä HTTP-liikennettä on helppo seurata Firebugilla tai Tamper Data -laajennuksella .
Selain lähettää WWW-palvelimelle pyynnön:
GET /sovellukset/testi.html HTTP/1.1 Connection: keep-alive Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Accept-Encoding: gzip,deflate Accept-Language: en-us,en;q=0.5 Host: appro.mit.jyu.fi Referer: http://appro.mit.jyu.fi/ User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4) Gecko/20030624 Keep-Alive: 300
GET /sovellukset/testi.html HTTP/1.0 | Pyydetään informaatiota (GET), jonka osoite palvelimella on /sovellukset/testi.html käyttäen HTTP/1.1-protokollaa. |
Connection: keep-alive | Käsketään pitää yhteys päällä. |
Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, video/x-mng, image/png, image/jpeg, image/gif;q=0.2, */*;q=0.1 | Luetellaan selaimen ymmärtämät mediatyypit. Kerrotaan myös mitä mediatyyppejä suositaan q-arvojen avulla. |
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 | Luetellaan kelpaavat merkistöt. |
Accept-Encoding: gzip,deflate | Luetellaan tuetut koodaustavat (pakkaus). |
Accept-Language: en-us,en;q=0.5 | Halutut kielet |
Host: appro.mit.jyu.fi | Palvelimen nimi |
Referer: http://appro.mit.jyu.fi/ | Osoite, josta ollaan tulossa (sivu, jolla olevaa linkkiä on klikattu). |
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.4) Gecko/20030624 | Selaimen tiedot |
Keep-Alive: 300 | Kertoo kuinka monta sekuntia yhteyttä pidetään yllä. Vain HTTP/1.0-protokollassa, mutta käytetään yhteensopivuussyistä. Oikeastaan muoto pitäisi olla Keep-Alive: parametri=arvo. |
Palvelin vastaa:
HTTP/1.1 200 OK Connection: close Date: Tue, 06 Apr 2004 10:53:18 GMT Accept-Ranges: bytes ETag: "2da29-2147-4044819d" Server: Apache/1.3.27 (Unix) (Red-Hat/Linux) PHP/4.1.2 mod_perl/1.26 Vary: Accept Content-Length: 414 Content-Type: application/xhtml+xml; charset=iso-8859-1 Last-Modified: Tue, 02 Mar 2004 12:44:13 GMT <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi" > <head> <title>Testisivu</title> <link href="testi.css" rel="StyleSheet" type="text/css" /> </head> <body> <h1>Testiotsikko</h1> <p>Testitekstiä</p> </body> </html>
HTTP/1.1 200 OK | Statuskoodi - kaikki kunnossa. |
Connection: close | Palvelin sulkee yhteyden. Normaalisti yhteys on pysyvä (persistent), jotta selain voi tehdä useita pyyntöä (kuvat, css, javascript jne.). |
Date: Tue, 06 Apr 2004 10:53:18 GMT | Päivä ja aika, koska vastaus on lähetetty. |
Accept-Ranges: bytes | Sallitaan tavuina määritellyt pyynnöt joissa halutaan vain osa dokumentista. |
ETag: "2da29-2147-4044819d" | Tunniste. Tunniste muuttuu dokumenttia muutettaessa. |
Server: Apache/1.3.27 (Unix) (Red-Hat/Linux) AxKit/1.62 mod_python/2.7.8 Python/1.5.2 mod_ssl/2.8.12 OpenSSL/0.9.6b DAV/1.0.3 PHP/4.1.2 mod_perl/1.26 mod_throttle/3.1.2 | WWW-palvelimen tiedot |
Vary: Accept-Encoding | Vaihteleeko pyydetty resurssi jonkun ehdon mukaan. Tärkeä tieto esim. välimuisteille. Accept-Encoding: esim. erilainen pakkaus. Accept-Language: tarjolla useita kieliversioita. User-Agent: sivun sisältö riippuu selaimesta. |
Content-Length: 414 | Sisällön koko |
Content-Type: application/xhtml+xml; charset=iso-8859-1 | Mediatyyppi ja merkistö |
Last-Modified: Tue, 02 Mar 2004 12:44:13 GMT | Milloin viimeksi muutettu. |
HTTP-otsikoita voi tutkia tarkemmin esim. seuraavilla ohjelmilla:
- Tamper Data -laajennos Firefoxiin
- Web-Sniffer
HTTP Status Codes
Palvelin palauttaa vastauksessa HTTP-statuskoodin, joka kertoo lyhyesti selaimelle miten pyyntö onnistui ja miten tulee jatkossa toimia. Listassa yleisimmät:
- 200 OK
- 300 Multiple Choices
- 301 Moved Permanently
- 302 Found
- 401 Unauthorized Access
- 403 Forbidden
- 404 Not Found
- 406 Not Acceptable
- 408 Request Time-out
- 500 Internal Server Error
Lisätietoa
Pythonin cgi-kirjasto
HTML:n erikoismerkit (<, > ja &) pitää muistaa koodata entiteeteiksi. Apuna voi käyttää cgi.escape-metodia. Attribuuttien arvot voi koodata xml.sax.saxutils.quoteattr-metodilla.
URLien kielletyt merkit voi koodata urllib.quote-metodilla.
Templatet ja Jinja
Yhtenäisen sivuston eri sivuilla on usein samankaltaisia osia, jotka joudutaan jokaisella sivulla liittämään includella tai copy&pastella jokaiselle sivulle. Näitä osia ovat esimerkiksi navigoinnit ja autentikoinnista huolehtiminen. Entäpä jos joltain sivulta tärkeitä kuten käyttäjätarkistus unohtuukin?
Templateilla (eli sivupohjilla) saavutetaan seuraavia etuja:
- Yhteiset osat kootaan samaan paikkaan
- Yhteiset osat liitetään automaattisesti sivulle
- Sivun tekijän tarvitsee keskittyä ainoastaan sisältöosaan
- Voidaan myös pyrkiä erottamaan ohjelmalogiikka esitystavasta.
- Erotetaan SQL:t ja ohjelmakoodit muualle
- Sivun sisällön, ulkoasun ja bisneslogiikan tuottajat voivat tehdä työtä erikseen
- Ei kannata kuitenkaan pyörää keksiä uudelleen - näiden välillä on aina jonkinlaisia kytköksiä
Sivun sisältämää html-koodia ei pidä kirjoittaa suoraan python-ohjelman sisään, koska silloin tuloksena on ylläpitokelvoton koodin ja html:n sekasotku. Pyritään MVC-malliin jossa ohjelman toimintalogiikka (controller) pidetään erossa näkymästä (view) eli www-sivun sisällöstä. Tämä onnistuu helposti käyttämällä templateja. Yksi käytetyimpiä on Jinja2.
Jinjan koodi erotetaan html-koodista jollakin seuraavista:
- {% ... %} ohjelmakoodi
- {{ ... }} yksittäisen muuttujan tulostaminen html:n sekaan
Muuttujien arvoja voi muokata Jinjan filttereillä. Filtteri ja muuttuja erotetaan toisistaan |-merkillä. Esim. lista|len palauttaa listan pituuden. Jos automatic escaping ei ole päällä niin jää omalle vastuulle varmistaa, että kaikkien muuttujien sisältö kelpaa html:ään. Tämä voidaan varmistaa e-filtterillä: muuttuja|e
Jinjan for-loopissa voi käyttää apuna loop-muuttujan arvoja. Esim. loop.index antaa 1-alkuisen kierroksen indeksin ja loop.index0 0-alkuisen. Jinja silmukkaa ei voi breakata.
Jos tarvii Jinjassa luoda uusia muuttujia niin se on tehtävä set-komennolla:
{% set muuttuja = arvo %}
Katso jinja.cgi-esimerkki (Lähdekoodi, Jinja template)
Toinen jinja-esimerkki (Lähdekoodi, Pohjatemplate, Toinen template)
Python, merkistöt ja erikoismerkit webissä
- 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 -*-
- Muista käyttää unicode-merkkijonoja:
right = u"Tämä on merkkijono" #unicode-merkkijono eli laita aina u-etuliite wrong = "Tämä on merkkijono" #bytestring - älä käytä
- 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-8
Edellä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>
Decode In Encode Out
Peruskaava on Decode in Encode out
- decode in
- Tiedostoista tulevat tekstit pitää dekoodata unicode-merkkijonoiksi:
io.open("file", encoding="UTF-8")
- lomakkeelta tulevat kentät pitää dekoodata unicode-merkkijonoiksi:
form.getfirst("kentta").decode("UTF-8")
- Kaikki ulkopuolelta tulevat merkkijonot pitää dekoodata. Monesti käytetyt kirjastot hoitavat tämän automaattisesti
- Tiedostoista tulevat tekstit pitää dekoodata unicode-merkkijonoiksi:
- Encode out
- lopullinen tulostettava (webbipalvelimen palauttama) merkkijono on enkoodattava UTF-8-merkistöön
print teksti.encode("UTF-8")
- lopullinen tulostettava (webbipalvelimen palauttama) merkkijono on enkoodattava UTF-8-merkistöön
- Komentoriviltä suoritettaessa python-tulkki itse hoitaa enkoodauksen käytetyn päätteen asetusten mukaan. Jos toimii komentoriviltä niin ei vielä tarkoita, että toimisi webbipalvelimella/selaimessa
Käyttäjien kommentit