4 in 1 - Medieninformatik - Hochschule RheinMain
4 in 1 - Medieninformatik - Hochschule RheinMain
4 in 1 - Medieninformatik - Hochschule RheinMain
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
Organisatorisches<br />
Organisatorisches<br />
Web-basierte Anwendungen<br />
Grundlagen und Frameworks<br />
Prof. Dr. Peter Barth<br />
<strong>Hochschule</strong> Rhe<strong>in</strong>Ma<strong>in</strong><br />
Fachbereich Design Informatik Medien<br />
Medien<strong>in</strong>formatik<br />
24. April 2013<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 1 / 91<br />
Vorlesung<br />
• Mittwochs, Raum 14, 8:15 – 9:45 Uhr<br />
Praktikum<br />
• Mittwochs, Raum 13: B ab 11:45 Uhr, A ab 14:15 Uhr, C ab 16:00 Uhr<br />
Vorlesungsfolien, Übungsblätter, weitere Informationen<br />
• http://read.mi.hs-rm.de<br />
• http://www.mi.hs-rm.de/˜barth/hsrm/webanw<br />
Bewertung<br />
• Prüfungsleistung, Mündliche Prüfung 70 %<br />
• Studienleistung, Praktikum 30 %<br />
• Projekt Web Python/Basis, m<strong>in</strong>destens 8 von 15 Punkten<br />
• Projekt Web Servlet/Framework, m<strong>in</strong>destens 8 von 15 Punkten<br />
Infrastruktur<br />
• Pool und/oder fertiges Image /opt/share/downloads/l<strong>in</strong>ux/ZuHause<br />
• Jeder <strong>in</strong>dividuell und die Projektgruppe(n) e<strong>in</strong> Repository<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 2 / 91<br />
Ziele der Veranstaltung<br />
Organisatorisches<br />
Voraussetzungen<br />
Organisatorisches<br />
Design und Realisierung Web-basierter Anwendungen<br />
• E<strong>in</strong>satzgebiete Web-basierter Anwendungen erkennen<br />
• Problemadäquater Entwurf, Architektur und Technologie- Frameworkwahl<br />
• Integration externer Dienste und Anwendungen, z.B. DBMS<br />
• Sicherheitsaspekte, Lastaspekte<br />
Umgang mit praxisrelevanten Web-Technologien<br />
• Grundlagen (CGI, Templat<strong>in</strong>g, Integration): mit Python<br />
Bewußt ohne (m<strong>in</strong>imal) weitere Frameworks (selber machen)<br />
• Applikationsserver/Servlet-Conta<strong>in</strong>er: mit Tomcat<br />
• Webkomponentensystem: mit Java Server Faces<br />
Vorbereitung Praxisphase und Beruf<br />
• Oft wichtigstes Thema<br />
• Im Praktikum dann meist auch Django (Python + Framework), Rails (Ruby), PHP<br />
mit Zend oder Typo3, . . .<br />
• . . . womit Sie nach Veranstaltung nach 1 Woche produktiv arbeiten können<br />
Auszeichnungssprachen<br />
• HTML Grundkenntnisse<br />
(de.selfhtml.org)<br />
• XML Grundkenntnisse<br />
Programmieren<br />
• Python, Java<br />
• OO, UI-Patterns<br />
Datenbanken<br />
• SQL<br />
• Integration <strong>in</strong><br />
Programmiersprachen<br />
Spaß<br />
• An komplexen D<strong>in</strong>gen<br />
• An der Umsetzung<br />
Infos, Tools und Downloads:<br />
/opt/share/praktika/WebAnw/<br />
/opt/share/downloads/l<strong>in</strong>ux/ZuHause<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 3 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 4 / 91
Organisatorisches<br />
Grundlagen Web<br />
E<strong>in</strong>führung<br />
Literatur<br />
E<strong>in</strong>führung<br />
CGI, Grundlagen<br />
• http://de.selfhtml.org/servercgi/cgi/<strong>in</strong>dex.htm<br />
• http://docs.python.org/2/library/cgi.html<br />
• http://www.python.org/dev/peps/pep-0333,<br />
http://code.google.com/p/modwsgi/<br />
• Programm<strong>in</strong>g Python, O’Reilly, Lutz, Kapitel 16<br />
Servlets<br />
• Servlets & JSP, Addison-Wesley, Falkner<br />
• JAVAEE/servlet, JavaEE/jsp<br />
http://jcp.org/en/jsr/detail?id=315<br />
http://jcp.org/en/jsr/detail?id=245<br />
Java Server Faces<br />
• Java Server Faces 2.0, dpunkt, Mar<strong>in</strong>scheck et. al<br />
• JAVAEE/javaserverfaces-139869.html,<br />
http://jcp.org/en/jsr/detail?id=314<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 5 / 91<br />
Web-basierte Anwendung<br />
• Dokumentenbasiert, Client-Server<br />
Client<br />
• Präsentation, Dokumentendarstellung<br />
• Web-Browser<br />
• Desktop, mobiles Device<br />
Server<br />
• Anwendungsfunktionalität,<br />
Dokumentenerzeugung<br />
• Web-Server<br />
• Meist Server<br />
Klassifikation<br />
• Inhalte, Layout, Interaktion<br />
• Anzahl Nutzer, Zugriffe<br />
• Sicherheit, Verfügbarkeit<br />
Anfrage<br />
Server<br />
Antwort<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 6 / 91<br />
Grundlagen Web<br />
E<strong>in</strong>führung<br />
Grundlagen Web<br />
E<strong>in</strong>führung<br />
Statische Inhalte<br />
Historie<br />
• 88, Tim Berners-Lee, Enquire<br />
• Hypertext,<br />
WWW<br />
• 90, Name “World Wide Web”<br />
• Standards HTTP, URI, HTML<br />
Übertragungsprotokoll<br />
• HTTP, Hypertext Transfer Protocol<br />
E<strong>in</strong>deutiger Zugriff auf Ressourcen<br />
• URI (Bezeichner), Uniform Resource<br />
Identifier<br />
• URL (Locator), e<strong>in</strong> URI<br />
Dokumentenformat<br />
• HTML, Hypertext Markup Language<br />
HTTP-Client (Web Browser)<br />
www.html<br />
Anfrage<br />
http://de.selfhtml.org/<strong>in</strong>tro/<br />
<strong>in</strong>ternet/www.html<br />
de.selfhtml.org<br />
Http-Server (Web Server)<br />
Antwort<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 7 / 91<br />
Dynamische Inhalte<br />
Statische Dokumente<br />
• Dokument liegt vorbereitet vor<br />
• Ke<strong>in</strong>e Personalisierung, ke<strong>in</strong>e<br />
zustandsabhängigen Seiten, ke<strong>in</strong>e<br />
Interaktion<br />
Dokumente dynamisch generieren<br />
• Erzeugen des Dokuments für die<br />
Antwort der Anfrage<br />
• Durch Ausführen e<strong>in</strong>es Programms<br />
unter Berücksichtigung des Zustands<br />
• Programm-Ausgabe ist HTML<br />
Client/Server<br />
• Web-Server weiß wann was generiert<br />
wird und kommuniziert mit Programm<br />
• Web-Client (Browser) merkt davon<br />
nichts<br />
HTTP-Client (Web Browser)<br />
„dynamisch<br />
generiert“<br />
Anfrage<br />
http://www.mi.hs-rm.de/portal/<br />
www.mi.hs-rm.de<br />
Http-Server (Web Server)<br />
Konfiguration<br />
Konfiguration<br />
Antwort<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 8 / 91
Grundlagen Web<br />
E<strong>in</strong>führung<br />
Grundlagen Web<br />
E<strong>in</strong>führung<br />
Dynamische Inhalte – Server<br />
Infrastruktur an der <strong>Hochschule</strong><br />
Common Gateway Interface (CGI)<br />
• E<strong>in</strong>fache Integration, mit allen<br />
Programmen und Umgebungen<br />
möglich, meist Skriptsprachen<br />
• Aufruf e<strong>in</strong>es externen Programms<br />
(separater Prozess) je Anfrage<br />
• Kommunikation über std<strong>in</strong>/stdout und<br />
Umgebungsvariablen<br />
Integration <strong>in</strong> Web-Server<br />
• Programm und Ablaufumgebung<br />
(Interpreter) läuft im Web-Server<br />
• Effizienter, e<strong>in</strong> Prozess mehrere<br />
Anfragen<br />
Separater Conta<strong>in</strong>er<br />
• Eng <strong>in</strong>tegriert mit HTTP-Server oder<br />
eigener HTTP-Server<br />
• Zusätzliche Dienste (Skalierung,<br />
Sicherheit, . . . ), Ablaufumgebung<br />
HTTP-Server<br />
Prozess<br />
HTTP-Server<br />
Prozess<br />
HTTP-Server<br />
HTTP-Server<br />
Conta<strong>in</strong>er<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 9 / 91<br />
Grundlagen Web<br />
Dynamische Inhalte – Client<br />
E<strong>in</strong>führung<br />
Host<br />
Host<br />
Host<br />
Host: www.mi.hs-rm.de<br />
Web-Server: Apache, httpd<br />
• ˜ss<strong>in</strong>n001/public_html/<br />
• Verfügbar als http.../˜ss<strong>in</strong>n001/<br />
CGI-Programme<br />
• ˜ss<strong>in</strong>n001/public_html/cgi-b<strong>in</strong>/p.cgi<br />
• Verfügbar als CGI-Programm<br />
http.../˜ss<strong>in</strong>n001/cgi-b<strong>in</strong>/p.cgi<br />
• Nur <strong>in</strong> public_html/cgi-b<strong>in</strong><br />
Integration <strong>in</strong> Web-Server, Python<br />
• Web Server Gateway Interface (WSGI)<br />
• Unter Apache Benutzerkennung<br />
• ˜ss<strong>in</strong>n001/public_html/wsgi/app.wsgi<br />
http.../˜ss<strong>in</strong>n001/wsgi/app.wsgi<br />
• E<strong>in</strong>e WSGI-Datei je App<br />
httpd (apache2)<br />
Prozess<br />
httpd (apache2)<br />
Prozess<br />
Prozess<br />
www<br />
log<strong>in</strong>3<br />
mi12<br />
www<br />
log<strong>in</strong>3<br />
mi12<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 10 / 91<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Erzeugen dynamischer Web-Seiten auf dem Server – CGI<br />
Roundtrips Visualisierung<br />
Common Gateway Interface (CGI)<br />
• Problem: Jede Interaktion bed<strong>in</strong>gt<br />
Server-Kommunikation, auch re<strong>in</strong>e<br />
Visualisierung<br />
• Lösung<br />
• Skript (Javascript) im Web-Browser<br />
• Veränderung der Anzeige durch<br />
Manipulation des DOM<br />
Roundtrips Flackern, wenig Daten<br />
• Problem: Neuaufbau Seite bei<br />
Server-Zugriff (Usability), vorhandene<br />
Daten neu übertragen<br />
• Lösung<br />
• Skript (Javascript) im Web-Browser<br />
• Geänderte Daten nachladen und<br />
Aktualisieren Manipulation DOM<br />
• Daten asynchron über HTTP<br />
Beispiel<br />
Autocompletion<br />
HTML<br />
HTTP-Server<br />
XML(Daten)<br />
Asynchronuous Javascript and XML<br />
(AJAX)<br />
• In Veranstaltung nur Verwendung,<br />
nicht wie es funktioniert<br />
• Starten externer Programme zur<br />
Dokumentenerzeugung<br />
• Kommunikation über<br />
Umgebungsvariablen und<br />
Standarde<strong>in</strong>gabe / Standardausgabe<br />
• Standardisiert<br />
Vorteil<br />
• Funktioniert <strong>in</strong> jeder Umgebung, mit<br />
jeder Programmiersprache<br />
• Darauf aufbauend alles realisierbar<br />
Nachteil<br />
• Ressourcen-<strong>in</strong>tensiv, e<strong>in</strong> neuer<br />
Prozess je Anfrage<br />
• Ke<strong>in</strong>e weitergehenden Features<br />
Anfrage<br />
HTTP-Server<br />
Prozess<br />
Antwort<br />
Host<br />
Umgebungsvariablen<br />
Standardausgabe<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 11 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 12 / 91
Grundlagen Web<br />
Common Gateway Interface<br />
Grundlagen Web<br />
Common Gateway Interface<br />
CGI-Programm <strong>in</strong> Python<br />
Ausgabe auf Standardausgabe<br />
• Erst Header, dann Content<br />
(HTTP-Protokoll)<br />
• Getrennt durch e<strong>in</strong>e Leerzeile (Extra<br />
newl<strong>in</strong>e Zeile 2)<br />
HTTP-Header<br />
• Content-Type zw<strong>in</strong>gend notwendig,<br />
meist text/html<br />
• Weitere Header-Zeilen möglich,<br />
werden meist von Web-Server ergänzt<br />
HTML-Dokument, Content<br />
• E<strong>in</strong>fach pr<strong>in</strong>t auf Standardausgabe<br />
• Beliebiger Code<br />
Fehler: “Internal Server Error”<br />
• Vielleicht noch was im Log...<br />
hallo.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 pr<strong>in</strong>t ""<br />
4 pr<strong>in</strong>t "Hallo"<br />
5 pr<strong>in</strong>t ""<br />
6 pr<strong>in</strong>t "Hallo CGI Python"<br />
7 pr<strong>in</strong>t ""<br />
CGI Beispiel – Details<br />
Anfrage<br />
1 GET<br />
2 /~pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
3 HTTP/1.1<br />
Umgebungsvariablen<br />
1 SERVER_SOFTWARE=Apache/2.2.14 ....<br />
2 SCRIPT_NAME=/~pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
3 REQUEST_METHOD=GET<br />
4 SERVER_PROTOCOL=HTTP/1.1<br />
5 HTTP_CONNECTION=Keep-Alive<br />
6 REMOTE_ADDR=172.26.33.61<br />
7 SERVER_PORT=80<br />
8 SERVER_ADDR=195.72.105.32<br />
9 DOCUMENT_ROOT=/var/www/<br />
10 HTTP_HOST=www<br />
11 GATEWAY_INTERFACE=CGI/1.1<br />
12 REMOTE_PORT=53197<br />
Antwort<br />
1 HTTP/1.1 200 OK<br />
2 Date: Mon, 04 Feb 2013 10:21:42 GMT<br />
3 Content-Encod<strong>in</strong>g: gzip<br />
4 Connection: Keep-Alive<br />
5 Content-Length: 82<br />
6 Server: Apache/2.2.14 (Ubuntu) ...<br />
7 Vary: Accept-Encod<strong>in</strong>g<br />
8 Content-Type: text/html<br />
9 Keep-Alive: timeout=15, max=100<br />
10<br />
11 ...<br />
Standardausgabe<br />
1 Content-Type: text/html<br />
2<br />
3 <br />
4 Hallo<br />
5 <br />
6 Hallo CGI Python<br />
7 <br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 13 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 14 / 91<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Komfortable CGI-Programmierung<br />
Umgebungsvariablen<br />
Python-Bibliothek<br />
• cgi Hilfsrout<strong>in</strong>en<br />
• cgitb (TraceBack), Ansicht<br />
Fehlermeldungen, sehr s<strong>in</strong>nvoll<br />
hallokomf.cgi<br />
Informationen <strong>in</strong> Umgebungsvariablen<br />
• Vom Web-Server, aus Anfrage<br />
• Vom Skript verwertbar<br />
Informationen – Anfrage<br />
• Anfragemethode, Query-Str<strong>in</strong>g, . . .<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 # immer gleich am Anfang<br />
Informationen – Infrastruktur<br />
3 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
4 import cgi, cgitb<br />
5 cgitb.enable()<br />
6<br />
7 # ab hier spezifisch je Skript<br />
8 fehler<br />
9 pr<strong>in</strong>t ""<br />
10 pr<strong>in</strong>t "Hallo"<br />
11 pr<strong>in</strong>t ""<br />
12 pr<strong>in</strong>t "Hallo CGI Python"<br />
13 pr<strong>in</strong>t ""<br />
Fehler <strong>in</strong> Zeile 8 wird erkannt und<br />
entsprechend im Browser ausgegeben.<br />
Sehr hilfreich beim Programmieren.<br />
• Web-Browser, Web-Server, . . .<br />
env.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import cgi, cgitb<br />
4 cgitb.enable()<br />
5 import os<br />
6 for key <strong>in</strong> os.environ:<br />
7 pr<strong>in</strong>t key, "=", os.environ[key]<br />
8 pr<strong>in</strong>t ""<br />
Beispiel ohne HTML-Rahmen. Viele Browser können daraus immer noch<br />
etwas machen. Für Vorlesung ok, da man ansonsten zu viel Boilerplate-Code hat.<br />
Sie generieren immer korrektes HTML.<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 15 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 16 / 91
Grundlagen Web<br />
Common Gateway Interface<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Anfragemethode GET<br />
Anfragemethode GET – Query-Str<strong>in</strong>g<br />
Uniform Resource Locator (URL)<br />
• Abruf e<strong>in</strong>er Ressource<br />
• Beispiel-URL<br />
http://www/˜pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
• Protokoll: HTTP<br />
• Server: www(.mi.hs-rm.de)<br />
• Pfad: /˜pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
HTTP-Protokoll GET<br />
• GET /˜pbart001/cgi-b<strong>in</strong>/hallo.cgi HTTP/1.1<br />
• Methode GET<br />
• Ressource /˜pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
• HTTP Protokoll Version 1.1<br />
Im Web-Server verpacken<br />
• In Umgebungsvariablen<br />
• POST Inhalte <strong>in</strong> std<strong>in</strong><br />
Anfrage<br />
GET<br />
~pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
HTTP/1.1<br />
HTTP-Server<br />
Prozess<br />
Antwort<br />
Host<br />
REQUEST_METHOD = GET<br />
SERVER_PROTOCOL = HTTP/1.1<br />
REQUEST_URI = /~pbart001/cgi-b<strong>in</strong>/hallo.cgi<br />
Ziel<br />
• Übergabe von zusätzlichen<br />
Informationen an Web-Server<br />
• Nur die Anfrage (Query) betreffend<br />
Query-Str<strong>in</strong>g – Anfrageparameter<br />
• Str<strong>in</strong>g an die URL angehängt<br />
• E<strong>in</strong>leitendes ?<br />
• Schlüssel/Wert-Paare durch & getrennt<br />
• Kodierung: ohne Änderung<br />
[a-zA-Z0-9]|’.’|’-’|’˜’|’_’,<br />
Leerzeichen als +, %FF HEX sonst<br />
Query-Str<strong>in</strong>g Integration bei CGI<br />
• Durch Umgebungsvariable<br />
• QUERY_STRING<br />
E<strong>in</strong>leitendes<br />
?<br />
?name=Tim&ende=ciao<br />
Schlüssel<br />
Wert<br />
name = Tim<br />
ende = ciao<br />
Trennendes<br />
&<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 17 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 18 / 91<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Grundlagen Web<br />
Common Gateway Interface<br />
Query-Str<strong>in</strong>g verwenden<br />
CGI-Bibliothek<br />
• Auslesen aus FieldStorage<br />
• Vorgefertigtes Aufbereiten<br />
querystr<strong>in</strong>g.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import cgi, cgitb<br />
4 cgitb.enable()<br />
5 import os<br />
6 form = cgi.FieldStorage() # Aufbereiten<br />
7 name, ende = "noname", "EOF"<br />
8 if "name" <strong>in</strong> form:<br />
9 name = form["name"].value<br />
10 if "ende" <strong>in</strong> form:<br />
11 ende = form["ende"].value<br />
12 pr<strong>in</strong>t "Hallo %s,%s" % (name, ende)<br />
13 pr<strong>in</strong>t "%s" % os.environ["QUERY_STRING"]<br />
Ohne Query-Str<strong>in</strong>g<br />
Query-Str<strong>in</strong>g ?name=Tim&ende=ciao<br />
Formulare erzeugen Query-Str<strong>in</strong>g<br />
Erzeugen Query-Str<strong>in</strong>g mit<br />
-Tag<br />
• action-Attribut: (relative)<br />
Ziel-URL<br />
• method-Attribut:<br />
Anfrage-Methode, get (oder<br />
post)<br />
E<strong>in</strong>gabefelder mit -Tag<br />
• type-Attribut: Welches<br />
E<strong>in</strong>gabefeld<br />
• text: Text-E<strong>in</strong>gabefeld<br />
• submit: Knopf<br />
• . . . SelfHTML<br />
Erzeugter Query-Str<strong>in</strong>g im Beispiel<br />
• name=Tim&ende=ciao&ok=Ok<br />
querystr<strong>in</strong>gform.html<br />
1 <br />
2 <br />
3 Name: <br />
4 Ende: <br />
5 <br />
6 <br />
7 <br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 19 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 20 / 91
Grundlagen Web<br />
Common Gateway Interface<br />
Grundlagen Web<br />
Formularelemente<br />
Parameter mit POST statt GET<br />
Formularelemente – Hausaufgabe<br />
POST statt GET<br />
• Philosophie: Daten schicken statt<br />
holen<br />
• Server-Zustand darf sich ändern<br />
• Wiederholte Anfragen können<br />
unterschiedliche Ergebnisse haben,<br />
Ergebnisse nicht cachen<br />
Eigenschaften POST<br />
• Verpacken auch <strong>in</strong> Str<strong>in</strong>g aus<br />
Schlüssel/Wert-Paaren, aber<br />
• Größere Datenmengen möglich (GET<br />
auf 2 KByte beschränkt), auch<br />
B<strong>in</strong>ärdaten (Bilder, etc.)<br />
• Nicht sichtbar <strong>in</strong> URL (nicht im<br />
Query-Str<strong>in</strong>g)<br />
• Verschlüsselt bei HTTPS<br />
Änderung <strong>in</strong> HTML-, nicht <strong>in</strong><br />
FieldStorage (ist für POST und GET)<br />
Weitergabe CGI über Standarde<strong>in</strong>gabe<br />
1 <br />
postparam.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import sys<br />
4 pr<strong>in</strong>t sys.std<strong>in</strong>.read()<br />
Wichtigste Formularelemente<br />
• E<strong>in</strong>zeiliges E<strong>in</strong>gabefeld<br />
• E<strong>in</strong>zeiliges E<strong>in</strong>gabefeld für Passwörter<br />
• Mehrzeiliges E<strong>in</strong>gabefeld<br />
• Mehrzeilige Auswahlliste<br />
• Dropdown-Box, e<strong>in</strong>zeilige Auswahlliste<br />
• Radiobuttons<br />
• Check Box<br />
• Versteckte E<strong>in</strong>gabewerte<br />
• Aktion ausführen, Knopf/Button<br />
In SelfHTML nachlesen und ausprobieren<br />
• Namen/Werte setzen, vorbelegen,<br />
formatieren<br />
• http://de.selfhtml.org<br />
/html/formulare/<strong>in</strong>dex.htm<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 21 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 22 / 91<br />
Grundlagen Web<br />
Formularelemente<br />
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Formulare und Mehrfachauswahl<br />
Mehrfachauswahl<br />
• Derselbe Schlüssel mehrfach <strong>in</strong><br />
Formular verwendet<br />
• Bei Radiobuttons natürlicherweise<br />
• Mehrere Ergebnisse <strong>in</strong> Parameter je<br />
Schlüssel möglich<br />
• Muss <strong>in</strong> Anwendungsprogramm<br />
berücksichtigt werden<br />
1 <br />
2 <br />
mehrfach.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
3 C <br />
4 Java <br />
5 Python <br />
6 <br />
7 <br />
8 <br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import os, cgi<br />
4<br />
5 form = cgi.FieldStorage()<br />
6 for skill <strong>in</strong> form.getlist("skill"):<br />
7 pr<strong>in</strong>t "Ich kann %s \n" % skill.capitalize()<br />
Erzeugen der Dokumente<br />
Dokumentenfragmente als Str<strong>in</strong>g im Quellcode :-(<br />
• HTML im Quellcode nicht als Markup zu erkennen<br />
• Markup-Quelle ist nicht validierbar<br />
• Ke<strong>in</strong>e Tool-Unterstützung beim Editieren<br />
• Vermischen von Anwendungslogik und Präsentation<br />
Ziel<br />
• Trennen von Anwendungslogik und<br />
HTML-Präsentation<br />
• HTML-Präsentation auslagerbar<br />
• Anwendungslogik frei von Markup<br />
Ansatz – Templat<strong>in</strong>g<br />
• Server Pages Konzept<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 typ = "Content-Type: "<br />
3 mime = "text/html"<br />
4 pr<strong>in</strong>t typ+mime+"\n"<br />
5<br />
6 pr<strong>in</strong>t ""<br />
7 pr<strong>in</strong>t ""<br />
8 pr<strong>in</strong>t "Hallo"<br />
9 pr<strong>in</strong>t ""<br />
10 pr<strong>in</strong>t ""<br />
11 pr<strong>in</strong>t "Hallo CGI Python"<br />
12 pr<strong>in</strong>t ""<br />
• Template-Eng<strong>in</strong>es<br />
Achtung: Art der Dokumentenerstellung unabhängig von<br />
technischer Integration (egal ob CGI oder proprietär)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 23 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 24 / 91
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Server Pages Konzept<br />
Server Pages – Anwendungsgebiet<br />
hello.sp<br />
Server Pages<br />
• E<strong>in</strong>bettung von Skript-Code <strong>in</strong><br />
Markup (HTML)<br />
• Zwischen speziellen Tags,<br />
z.B. oder <br />
• Verfügbar für viele Sprachen und<br />
Umgebungen<br />
Entwicklungssicht<br />
• Erstelle statische HTML-Seite<br />
• Spezieller Bereich “agiert dynamisch”<br />
Ausführungssicht<br />
• Übersetze HTML-Seite <strong>in</strong> Skript<br />
• HTML kopiert sich auf Ausgabe<br />
• Code bleibt erhalten<br />
• Führe entstehendes Skript aus<br />
1
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Templat<strong>in</strong>g mit Python<br />
E<strong>in</strong>fachst-Templat<strong>in</strong>g e<strong>in</strong>gebaut<br />
• Python Formatier-Operator %<br />
• Ersetzungsstelle <strong>in</strong> template durch<br />
%(name)s markieren<br />
• In Ersetzungs-Dictionary<br />
dic["name"] = wert setzen<br />
• Für mehrere Stellen und mehrere<br />
Name/Wert-Paare<br />
• E<strong>in</strong>setzen mit template % dic<br />
Template-Eng<strong>in</strong>es<br />
• Reichlich vorhanden, e<strong>in</strong> paar<br />
Dutzende <strong>in</strong><br />
http://wiki.python.org/mo<strong>in</strong>/Templat<strong>in</strong>g<br />
• Mehr Features, zum Beispiel Iterieren<br />
• Uns reicht E<strong>in</strong>fachst-Templat<strong>in</strong>g<br />
tpage.tpl<br />
1 <br />
2 %(title)s<br />
3 <br />
4 %(title)s<br />
5 %(tablecontent)s<br />
6 <br />
7 <br />
tabl<strong>in</strong>e.tpl<br />
1 <br />
2 %(first)s<br />
3 %(second)s<br />
4 <br />
Templat<strong>in</strong>g mit Python – Beispiel<br />
Beispiel<br />
• Template-Dateien vollständig e<strong>in</strong>lesen<br />
• Ersetze z.B. title durch Zeichen, zwei<br />
Mal, im Header und am Anfang der<br />
Seite<br />
• Typische letzte Zeile, vorher nie e<strong>in</strong>e<br />
Ausgabe<br />
tplzeichen.cgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import cgi, cgitb, str<strong>in</strong>g<br />
4 cgitb.enable()<br />
5<br />
6 tabl<strong>in</strong>e = file("tabl<strong>in</strong>e.tpl").read()<br />
7 page = file("tpage.tpl").read()<br />
8<br />
9 l<strong>in</strong>es = []<br />
10 for c <strong>in</strong> str<strong>in</strong>g.lowercase:<br />
11 dic = {’first’ : c,<br />
12 ’second’ : c.upper()}<br />
13 l<strong>in</strong>es.append(tabl<strong>in</strong>e%dic)<br />
14<br />
15 dic = {’title’: "Zeichen"}<br />
16 dic[’tablecontent’] = "\n".jo<strong>in</strong>(l<strong>in</strong>es)<br />
17<br />
18 pr<strong>in</strong>t page % dic<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 29 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 30 / 91<br />
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Grundlagen Web<br />
Templat<strong>in</strong>g<br />
Sichere Integration von Inhalten<br />
Komponenten zur Ansicht<br />
Korrekte/sichere Darstellung von Texten<br />
• Spezielle Regeln für Markup (HTML)<br />
müssen e<strong>in</strong>gehalten werden (Aufgabe<br />
des Template-Autors)<br />
• Benutzerdef<strong>in</strong>ierte Inhalte (potenziell<br />
böse) müssen <strong>in</strong>tegriert werden<br />
Probleme – böse Inhalte<br />
• Browserland ist Fe<strong>in</strong>desland<br />
• Texte mit Sonderzeichen/Tags<br />
Vorgefertigte Funktionen verwenden<br />
• Beispiel “Quotieren” von Str<strong>in</strong>gs mit<br />
cgi.escape, Sonderzeichen und<br />
Tag-Zeichen () umwandeln<br />
• E<strong>in</strong>fügen von Skript-Code verh<strong>in</strong>dert<br />
• Weiteres: Entfernen von Tags, . . .<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 # -*- cod<strong>in</strong>g: utf-8 -*-<br />
3 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
4 import os, cgi<br />
5 l<strong>in</strong>es = ["Hallo", "Hällo", "", " bold ", ’a " double quote’]<br />
6 for l<strong>in</strong>e <strong>in</strong> l<strong>in</strong>es:<br />
7 pr<strong>in</strong>t "%s " % l<strong>in</strong>e<br />
8 pr<strong>in</strong>t<br />
9 for l<strong>in</strong>e <strong>in</strong> l<strong>in</strong>es:<br />
10 pr<strong>in</strong>t "%s " % cgi.escape(l<strong>in</strong>e, True)<br />
Hallo <br />
Hällo <br />
<br />
bold <br />
a " double quote <br />
Hallo <br />
Hällo <br />
<tag> <br />
<b> bold </b> <br />
a " double quote <br />
Rendern von HTML beschränken<br />
• Manuelles Generieren von HTML<br />
unabhängig von Präsentationslogik<br />
• Re<strong>in</strong>es Rendern (Generieren) von<br />
HTML unabhängig, wiederverwendbar<br />
realisieren<br />
Beispiel – Tabelle<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
3 import cgi, cgitb, str<strong>in</strong>g<br />
4 cgitb.enable()<br />
5<br />
6 from tabelle import table<br />
7<br />
8 V,N,M = ’vorname’,’nachname’,’matnr’<br />
9 keys = [(V, ’Vorname’), (N, ’Nachname’), (M, ’MatNr’)]<br />
10 data = [{V: ’Susi’, N: ’S<strong>in</strong>nlos’, M: ’135789’},<br />
11 {V: ’Rudi’, N: ’Ratlos’, M: ’222333’},<br />
12 {V: ’WWW’, N: ’WickedWestWitch’, M: ’666666’}]<br />
13 ctable = table(keys, data)<br />
14 c = "Tabelle%s" % ctable<br />
15 pr<strong>in</strong>t c<br />
1 # Markup nur konstant, auslagerbar/aenderbar<br />
2 _table = "\n%(header)s\n%(data)s\n\n"<br />
3 _row = "%s"<br />
4 _header = "%s"<br />
5 _entry = "%s"<br />
6<br />
7 def _rowformat(l<strong>in</strong>e, keys, wrap):<br />
8 entries = [wrap % l<strong>in</strong>e[t[0]] for t <strong>in</strong> keys]<br />
9 return _row % " ".jo<strong>in</strong>(entries)<br />
10<br />
11 def table(keys, data):<br />
12 dic = dict()<br />
13 hdict = {}<br />
14 for tup <strong>in</strong> keys:<br />
15 hdict[tup[0]] = tup[1]<br />
16 dic["header"] = _rowformat(hdict, keys, _header)<br />
17 rows = [_rowformat(l<strong>in</strong>e, keys, _entry)<br />
18 for l<strong>in</strong>e <strong>in</strong> data]<br />
19 dic["data"] = "\n".jo<strong>in</strong>(rows)<br />
20 return _table % dic<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 31 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 32 / 91
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Vor- und Nachteile von CGI<br />
Vorteile<br />
• E<strong>in</strong>fach, standardisiert<br />
• Funktioniert immer, mit jeder Programmiersprache<br />
• Ausführung unter der eigenen Benutzerkennung<br />
Nachteile<br />
• Je Anfrage e<strong>in</strong> Prozess, Last<br />
• Lösungsansatz: FastCGI, mehrere Anfragen je<br />
Prozess, vermeidet Start der Ablaufumgebung<br />
• Lösungsansatz: Integration <strong>in</strong> Web-Server, meist<br />
proprietär, Interpreter/Runtime läuft im Web-Server,<br />
mehr Features möglich<br />
• Ke<strong>in</strong>e Trennung Anwendungslogik und<br />
Präsentationslogik erzwungen<br />
• Verleitet zu Spaghetti-Code (muss aber nicht se<strong>in</strong>)<br />
E<strong>in</strong>satzgebiet<br />
• E<strong>in</strong>fache Anwendungen<br />
mit wenigen<br />
dynamischen Seiten<br />
• Darstellung und<br />
E<strong>in</strong>gabe von<br />
strukturierten Daten<br />
• E<strong>in</strong>fache<br />
Wiederverwendung von<br />
vorhandenem<br />
Anwendungs-Code<br />
Beispiele<br />
• Bugzilla, Bugtrack<strong>in</strong>g<br />
Tool, Perl<br />
• Mo<strong>in</strong>Mo<strong>in</strong>, Wiki, Python<br />
• . . .<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 33 / 91<br />
Integration von Python <strong>in</strong> Web-Server – WSGI<br />
Ziele<br />
• Integration so e<strong>in</strong>fach wie CGI<br />
• Ke<strong>in</strong> neuer Interpreter je Aufruf, e<strong>in</strong><br />
Prozess für mehrere Anfragen<br />
• Unabhängig vom Web-Framework<br />
Web Server Gateway Interface (WSGI)<br />
• Python-Schnittstelle zur Integration <strong>in</strong><br />
Web-Server, Teil der<br />
Standardbibliothek<br />
• Gut dokumentiert, PEP-0333 (2003)<br />
• Akzeptiert: Von (fast) allen<br />
Frameworks und Http-Servern<br />
unterstützt www.wsgi.org<br />
mod_wsgi für Apache-Integration<br />
• code.google.com/p/modwsgi/<br />
• Proprietäre effiziente Integration<br />
HTTP-Server<br />
WSGI<br />
Python-Prozess<br />
Python-Prozess<br />
Apache-HTTP-Server<br />
mod_wsgi<br />
(WSGI-Gateway)<br />
WSGI-Protokoll<br />
Python-Prozess<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 34 / 91<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Ablauf – WSGI im Web-Server<br />
WSGI<br />
Anfrage von Client im Web-Server<br />
• Skript jetzt.wsgi wird verwendet<br />
• Warum konfigurierbar (kommt noch),<br />
zum Beispiel anhand von Date<strong>in</strong>ame<br />
Übergabe an Python Prozess/Interpreter<br />
• Nur bei erstem Aufruf<br />
• Python soll Modul jetzt importieren<br />
• Python übersetzt Modul <strong>in</strong> Byte-Code<br />
und lädt Modul<br />
• Bei jedem Aufruf<br />
• Konfigurierte Funktion wird gerufen<br />
(zum Beispiel application)<br />
• Iterieren über Funktionsergebnis ist<br />
Ergebnisseite<br />
Gut für Betrieb, aber bei Entwicklung<br />
zunächst ke<strong>in</strong> Neuladen bei Änderung<br />
Python<br />
Bytecode<br />
Apache-HTTP-Server<br />
mod_wsgi<br />
(WSGI-Gateway)<br />
Python-Prozess<br />
Importieren,1x<br />
Übersetzen,<br />
1x<br />
Funktionsaufruf,<br />
jedes Mal<br />
Python<br />
Quellcode<br />
HTTP-Server mit WSGI liefert<br />
• environ Umgebungsvariablen<br />
• start_response Antwort-Funktion<br />
HTTP-Server mit WSGI erwartet<br />
• E<strong>in</strong>e Funktion<br />
• Params (environ, start_response)<br />
• Aufruf von start_response mit<br />
• status: HTTP Status, meist 200 OK<br />
• header: Liste von Schlüssel/Wert-<br />
Tupeln, zum Beispiel mit<br />
("Content-Type", "text/html")<br />
• Rückgabe e<strong>in</strong> iterierbares Objekt aus<br />
dem Dokument erzeugt wird<br />
Beispiel<br />
• jetzt.wsgi, aktuelles Datum/Uhrzeit<br />
jetzt.wsgi<br />
1 import datetime<br />
2 page = "%s"<br />
3 def app(environ, start_response):<br />
4 today=str(datetime.datetime.today())<br />
5 header=[("Content-Type","text/html")]<br />
6 start_response("200 OK", header)<br />
7 return [page % today]<br />
8<br />
9 application = app # mod_wsgi<br />
WSGI und Apache<br />
• mod_wsgi<br />
• Erwartet den Namen application<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 35 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 36 / 91
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
Proprietäre Integration<br />
URL auf WSGI-Skript abbilden<br />
Je nach URL andere Seiten erzeugen<br />
url.wsgi<br />
Abbildung bei CGI/WSGI<br />
• Konfiguration Web-Server<br />
• Meist automatische Assoziation von<br />
Skriptname im Pfad auf Skript<br />
URL-Mapp<strong>in</strong>g<br />
• Üblich bei statischen Inhalten:<br />
Assoziation zwischen Dateipfad und<br />
Zugriffspfad <strong>in</strong> URL<br />
1 def app(environ, start_response):<br />
2 rurl = environ["PATH_INFO"]<br />
3 page = "Rest-URL: %s" % rurl<br />
4 header=[("Content-Type","text/html")]<br />
5 start_response("200 OK", header)<br />
6 return [page]<br />
• E<strong>in</strong>geschränkt auf spezifisches<br />
Unterverzeichnis cgi-b<strong>in</strong> bzw. wsgi<br />
unterhalb von public_html mit<br />
Endung .cgi bzw. .wsgi<br />
• Assoziation abhängig von der<br />
Konfiguration des Http-Servers<br />
• Bei Anwendungen meist alles, bis auf<br />
statische Dateien, auf e<strong>in</strong> Skript<br />
7<br />
8 application = app # mod_wsgi<br />
• Achtung: Pfadangabe nach<br />
Skriptname möglich (und s<strong>in</strong>nvoll)<br />
• (Rest-)URL kann verwendet werden<br />
um andere Seiten zu generieren<br />
Im Produktivbetrieb nicht sichtbar<br />
Konfiguration an <strong>Hochschule</strong><br />
• Web-Server Konfiguration (z.B.<br />
Apache mod_rewrite), alle Anfragen<br />
auf e<strong>in</strong> Skript, s<strong>in</strong>nvoll<br />
• <strong>Hochschule</strong>: Angabe von Skript<br />
http://www.mi.hs-rm.de/<br />
˜ss<strong>in</strong>n001/wsgi/jetzt.wsgi<br />
• Skriptname unterhalb von<br />
public_html/wsgi mit Endung .wsgi<br />
• Alles “darunter” auch! URL darf weiter<br />
gehen<br />
Bei cgi gleiches Verhalten<br />
Mapp<strong>in</strong>g: Je nach URL andere Funktion<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 37 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 38 / 91<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
Proprietäre Integration<br />
GET Anfragen mit WSGI<br />
POST-Anfragen mit WSGI<br />
get.wsgi<br />
postparam.wsgi<br />
GET Anfragen mit WSGI<br />
• Zugriff auf Query-Str<strong>in</strong>g wie bei cgi,<br />
parse_qs auch bei cgi<br />
• parse_qs liefert Dict. aus Inhalten des<br />
Query-Str<strong>in</strong>gs, Achtung: Liste als<br />
Wert, da mehrere E<strong>in</strong>träge erlaubt s<strong>in</strong>d<br />
• Abhängig von Query-Str<strong>in</strong>g Inhalten<br />
unterschiedliche Seiten generieren<br />
1 from urlparse import parse_qs<br />
2 def application(environ, start_response):<br />
3 query=parse_qs(environ[’QUERY_STRING’])<br />
4 content = "Bye"<br />
5 if "hi" <strong>in</strong> query:<br />
6 content = hi(query)<br />
7 header=[("Content-Type","text/html")]<br />
8 start_response("200 OK", header)<br />
9 return [content]<br />
10 def hi(query):<br />
11 name = "niemand"<br />
12 if "name" <strong>in</strong> query:<br />
13 name = "".jo<strong>in</strong>(query["name"])<br />
14 return "Hallo, %s\n" % name<br />
Rückrichtung mit urllib.urlencode<br />
• Folge von Schlüssel/Wert-Paaren als<br />
e<strong>in</strong> Query-Str<strong>in</strong>g kodieren<br />
• Tupel können Sonderzeichen<br />
enthalten, die encodiert werden<br />
• Leerzeichen als +, & als %26<br />
• %XY generische Kodierung<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 39 / 91<br />
POST Anfragen mit WSGI<br />
• Änderung Formular für POST<br />
<br />
• Zugriff auf Query-Str<strong>in</strong>g durch Lesen<br />
des E<strong>in</strong>gabestroms<br />
• Verarbeiten wie bei GET mit parse_qs<br />
1 from urlparse import parse_qs<br />
2<br />
3 def app(environ, start_response):<br />
4 name, ende = ["niemand"], ["None"]<br />
5 post = environ[’wsgi.<strong>in</strong>put’].read()<br />
6 dic = parse_qs(post)<br />
7 if "name" <strong>in</strong> dic: name = dic["name"]<br />
8 if "ende" <strong>in</strong> dic: ende = dic["ende"]<br />
9 tup = (name[0], ende[0])<br />
10 content = "Hallo %s, %s" % tup<br />
11 header=[("Content-Type","text/html")]<br />
12 start_response("200 OK", header)<br />
13 return [content]<br />
14<br />
15 application = app # mod_wsgi<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 40 / 91
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Lokale WSGI-Umgebung zur Entwicklung<br />
Entwicklung<br />
• Integration <strong>in</strong> HTTP-Server<br />
(Conta<strong>in</strong>er) kompliziert und für<br />
Entwicklung nicht notwendig<br />
• E<strong>in</strong>facher HTTP-Server Teil der<br />
Python Standardbibliothek<br />
• Nutzung mit Paket wsgiref<br />
Paket wsgiref<br />
• Referenzimpl. von wsgi<br />
• simpleserver für Entwicklung<br />
• make_server erstellt<br />
HTTP-Server mit<br />
WSGI-Anwendung<br />
• Servername: localhost<br />
• Port (beliebig): 8080<br />
• app: WSGI-Anwendung<br />
envwsgiref.wsgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2<br />
3 def app(env, start_response):<br />
4 header=[("Content-Type","text/html")]<br />
5 content = ["%s: %s " % (k,env[k]) for k <strong>in</strong> env]<br />
6 start_response("200 OK", header)<br />
7 return content<br />
8<br />
10<br />
9 application=app<br />
11 if __name__ == ’__ma<strong>in</strong>__’:<br />
12 from wsgiref.simple_server import make_server<br />
13 httpd = make_server(’localhost’, 8080, app)<br />
14 httpd.serve_forever()<br />
mod_wsgi Kompatibilität<br />
• app <strong>in</strong> application umbenennen<br />
• if: Anwendung mit mod_wsgi im Apache und<br />
wsgiref lokal ausführbar<br />
Benutzung wsgiref<br />
Starten als lokales Python Programm<br />
• python envwsgiref.wsgi<br />
• Endung .wsgi nur für mod_python,<br />
sonst auch .py möglich<br />
• Log-Ausgabe je Anfrage auf Konsole<br />
Benutzung mit IDEs<br />
• Idle, Emacs oder Eclipse (PyDev)<br />
• Debugg<strong>in</strong>g unterstützt, e<strong>in</strong>fach<br />
Breakpo<strong>in</strong>t <strong>in</strong> Anwendung setzen und<br />
mit Browser auf Anwendung zugreifen<br />
• PyDev<br />
• Endung muss .py se<strong>in</strong><br />
• Separate .wsgi-Datei, importiert nur<br />
application-Objekt<br />
• Beispiel env2.py und env2.wsgi<br />
• Testen mit env2.py<br />
env2.wsgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 import sys, os<br />
3 sys.path.append(os.path.dirname(__file__))<br />
4 os.chdir(os.path.dirname(__file__))<br />
5 from env2 import application<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 41 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 42 / 91<br />
Grundlagen Web<br />
Proprietäre Integration<br />
Grundlagen Web<br />
werkzeug<br />
pydev – Python mit Eclipse<br />
Abstraktion von der dokumentenbasierten Web-Schnittstelle<br />
PyDev<br />
Web-Anwendung<br />
Abstraktion bei Web-Frameworks<br />
• pydev.org<br />
• Eclipse-Plug<strong>in</strong>, über<br />
Eclipse-Marketplace<br />
verfügbar und von<br />
jedem selbst zu<br />
<strong>in</strong>stallieren<br />
• Vor Start e<strong>in</strong>mal<br />
Python-Interpreter<br />
setzen<br />
Python-Entwicklung<br />
• Completion<br />
• Debugg<strong>in</strong>g<br />
• Subversion<br />
• . . .<br />
Im Praktikum Idle oder Eclipse mit PyDev verwenden<br />
(oder natürlich Emacs).<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 43 / 91<br />
• Anfrage erzeugt Antwort,<br />
Textdokumente<br />
• Kommunikation <strong>in</strong> Anwendung h<strong>in</strong>e<strong>in</strong><br />
• Schlüssel/Wert-Paare<br />
• der E<strong>in</strong>gabestrom<br />
• Kommunikat. von Anwendung heraus<br />
• Schlüssel/Wert-Paare<br />
• (HTML-)Dokument<br />
Viele Konventionen:<br />
• → REQUEST_METHOD: GET<br />
bei GET-Anfrage<br />
• ← Content-Type: text/html<br />
bei HTML-Dokument als Antwort<br />
• . . .<br />
Nicht komfortabel<br />
• Request-Objekt, Response-Objekt;<br />
kapselt Anfrage, Antwort<br />
• Zugriff über Request-Objekt auf<br />
Eigenschaften der Anfrage, z.B.<br />
(Query)-Parameter, . . .<br />
• Verändern des Response-Objekt um<br />
dann Text-Antwort zu erzeugen, z.B.<br />
Typ setzen<br />
Web-Abstraktion/Frameworks <strong>in</strong> Python<br />
• werkzeug, e<strong>in</strong>fache WSGI-Abstraktion,<br />
verwenden wir<br />
• Alternative webob, nur Req.-/Resp.<br />
• django, Full-Stack Framework (sehr<br />
schön, aber zu Lehrzwecken zu groß<br />
und zu stark abstrahiert)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen • . . .<br />
24. April 2013 44 / 91
Grundlagen Web<br />
werkzeug<br />
Grundlagen Web<br />
werkzeug<br />
Abstraktion mit werkzeug<br />
werkzeug Entwicklung<br />
werkzeug<br />
• Weit verbreitete WSGI-Abstraktion,<br />
noch ke<strong>in</strong> Web-Framework<br />
• Python-Bibliothek<br />
Features<br />
• Request/Response Objekte<br />
• URL-Rout<strong>in</strong>g, welche Funktion für<br />
welche URL<br />
• Cookies, Sessions, . . .<br />
• Entwicklungsunterstützung:<br />
automatisches Neuladen, Tracebacks<br />
E<strong>in</strong>faches Beispiel:<br />
werkzeug.pocoo.org/docs/quickstart<br />
(Tutorial passt nicht)<br />
werkzeug.pocoo.org<br />
1 from werkzeug.wrappers import Request, Response<br />
2<br />
3 def app(environ, start_response):<br />
4 request = Request(environ)<br />
5 name = request.args.get("name", "Welt")<br />
6 response = Response("Hallo %s" % name)<br />
7 response.content_type="text/html"<br />
8 return response(environ, start_response)<br />
9<br />
10 if __name__ == ’__ma<strong>in</strong>__’:<br />
11 from wsgiref.simple_server import make_server<br />
12 httpd = make_server(’localhost’, 8080, app)<br />
13 httpd.serve_forever()<br />
Request/Response häufig gebraucht<br />
• @Request.application e<strong>in</strong>e Abkürzung<br />
• Nur noch e<strong>in</strong> Parameter, request<br />
• Neue Instanz von Response zurück<br />
werkzeug.serv<strong>in</strong>g, run_simple<br />
• Reloader: Bei Änderung der<br />
Quelldatei wird automatisch die<br />
Quell-Datei neu geladen; ke<strong>in</strong><br />
Neustart mehr notwendig<br />
• Debugger<br />
• Aussagekräftige Fehlermeldung<br />
• Cool: Interaktive Konsole im Browser<br />
• Nachteil: Debugger (Eclipse)<br />
funktioniert nicht mehr<br />
• Beides verwenden je nach Bedarf<br />
• Eclipse: use_reloader=False<br />
1 from werkzeug.wrappers import Request, Response<br />
2 @Request.application<br />
3 def app(request):<br />
4 name = request.args.get("name", "Welt")<br />
5 fehler<br />
6 return Response("Hallo %s" % name)<br />
7 if __name__ == ’__ma<strong>in</strong>__’:<br />
8 from werkzeug.serv<strong>in</strong>g import run_simple<br />
9 run_simple(’localhost’, 8080, app,<br />
10 use_reloader=True, use_debugger=True)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 45 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 46 / 91<br />
Grundlagen Web<br />
werkzeug<br />
Grundlagen Web<br />
werkzeug<br />
Standard WSGI-Rahmen<br />
Request-Objekt<br />
Für Deployment <strong>in</strong> Apache; für Entwicklung mit Debugg<strong>in</strong>g <strong>in</strong> Eclipse oder Entwicklung<br />
mit Traceback und Debug-Konsole im Browser<br />
webapp.wsgi<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 # -*- cod<strong>in</strong>g: utf-8 -*-<br />
3 import sys, os<br />
4 sys.path.append(os.path.dirname(__file__))<br />
5 if os.path.dirname(__file__):<br />
6 os.chdir(os.path.dirname(__file__))<br />
7 from webapp import application<br />
8<br />
9 # Für Eclipse ab hier noch <strong>in</strong> webapp.py (Syntax-Highlight<strong>in</strong>g, run)<br />
10 RUN_WERKZEUG=1 # oder 0 für wsgi_ref.simple_server<br />
11 if __name__ == ’__ma<strong>in</strong>__’:<br />
12 if RUN_WERKZEUG:<br />
13 from werkzeug.serv<strong>in</strong>g import run_simple<br />
14 run_simple(’localhost’, 8080, application,<br />
15 use_reloader=True, use_debugger=True)<br />
16 else:<br />
17 from wsgiref.simple_server import make_server<br />
18 httpd = make_server(’localhost’, 8080, application)<br />
19 httpd.serve_forever()<br />
Request-Objekt<br />
• request = Request(environ) (oder Parameter)<br />
• Erzeugt aus Umgebungsvariablen<br />
• Nur lesbar<br />
Attribute, Methoden:<br />
• Dokumentation:<br />
werkzeug.pocoo.org/docs/wrappers<br />
• method: GET, POST, . . .<br />
• args: Dictionary aus Query-Str<strong>in</strong>g (GET)<br />
• form: Dictionary aus Formulare<strong>in</strong>gaben (POST)<br />
• values: komb<strong>in</strong>iert args und form<br />
• url(base_url): URL (ohne Query-Str<strong>in</strong>g)<br />
• path: Angefragter Pfad<br />
• environ: Die Orig<strong>in</strong>al-Umgebungsvar.<br />
Web-Anwendung def<strong>in</strong>iert application, z.B. <strong>in</strong> Modul webapp(.py)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 47 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 48 / 91
Grundlagen Web<br />
werkzeug<br />
Grundlagen Web<br />
Rout<strong>in</strong>g<br />
Response-Objekt<br />
Rout<strong>in</strong>g – Welche Seite soll angezeigt werden?<br />
Response-Objekt<br />
Web-Anwendung<br />
Beispiel: Apache Web-Server <strong>Hochschule</strong><br />
• response = Response(); ... ;<br />
return response(environ, start_response)<br />
(oder return response)<br />
• Optional Inhalts-Str<strong>in</strong>g<br />
• Veränderbar, auch Liste als Inhalt<br />
Attribute, Methoden:<br />
• Dokumentation:<br />
werkzeug.pocoo.org/docs/wrappers<br />
• Iterable als Content (im Beispiel Liste)<br />
• status, status_code: HTTP-Status<br />
• headers: HTTP-Header<br />
• automatically_set_content_length:<br />
Größe (HTTP/1.1 offene Verb<strong>in</strong>dung!)<br />
• charset: ’utf-8’<br />
1 from werkzeug.wrappers import Request, Response<br />
2<br />
3 @Request.application<br />
4 def app(request):<br />
5 c = []<br />
6 response = Response(c)<br />
7 c.append("content modifiable\n")<br />
8 response.status = "200 OK"<br />
9 response.status_code = 200<br />
10 c.append("response.headers:\n")<br />
11 hs = response.headers<br />
12 for k <strong>in</strong> hs.keys():<br />
13 s=" %s: %s\n" % (k, hs[k])<br />
14 c.append(s)<br />
15 response.automatically_set_content_length = True<br />
16 return response<br />
Mehr mit Mix<strong>in</strong>s; Dokumentation<br />
• Mehrere “Seiten” (Formulare)<br />
• Nicht nur angepasst, sondern andere<br />
Aufgabe (Produkt vs. Warenkorb)<br />
• Je nach Anfrage (Rout<strong>in</strong>g) und<br />
Zustand der Anwendung wird e<strong>in</strong>e<br />
andere Seite ausgewählt<br />
Rout<strong>in</strong>g<br />
• Statische Seiten/Inhalte: Abbildung<br />
von Pfad <strong>in</strong> URL und Date<strong>in</strong>ame <strong>in</strong><br />
Ordnerhierarchie (z.B. <strong>in</strong>dex.html,<br />
/static/style.css)<br />
• Dynamisch: Abbildung von Pfad <strong>in</strong><br />
URL auf <strong>in</strong>haltsgenerierende<br />
Funktionalität (Skript)<br />
• von /˜/... auf<br />
user/public_html/... (unter anderem<br />
statische Inhalte wie Bilder, HTML-Seiten,<br />
Style-Sheets, JavaScript-Dateien, . . . )<br />
• von / /cgi-b<strong>in</strong>/.cgi/* auf<br />
CGI-Skript<br />
/˜user/public_html/cgi-b<strong>in</strong>/.cgi<br />
• von /˜/wsgi/.wsgi/* auf<br />
Python-Skript<br />
/˜user/public_html/wsgi/.wsgi<br />
• von allem mit Endung * .php auf<br />
e<strong>in</strong>gebetteten PHP-Interpreter<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 49 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 50 / 91<br />
Grundlagen Web<br />
Rout<strong>in</strong>g<br />
Grundlagen Web<br />
Rout<strong>in</strong>g<br />
Rout<strong>in</strong>g Beispiel – Statische Inhalte mit WSGI<br />
Optionen Code-Organisation<br />
Statische Inhalte<br />
• Statische Inhalte (Assets wie Bilder,<br />
Style-Sheets) müssen <strong>in</strong><br />
Web-Anwendung zur Verfügung<br />
gestellt werden<br />
• In Produktiv-Umgebungen häufig<br />
separater Server und separate URL,<br />
bzw. Apache speziell konfiguriert<br />
• Während Entwicklung mühselig<br />
• Ziel: statische Inhalte während<br />
Entwicklung<br />
Beispiel: E<strong>in</strong>faches statisches Rout<strong>in</strong>g<br />
• Wenn URL mit /static/ beg<strong>in</strong>nt,<br />
dann e<strong>in</strong>fach nur Inhalt wiedergeben<br />
• Ansonsten mit application<br />
weitermachen<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import datetime, os<br />
3 page = """Es ist %(uhr)s Uhr<br />
4 """<br />
5<br />
6 @Request.application<br />
7 def application(request):<br />
8 if request.path.startswith("/statisch"):<br />
9 return statisch(request)<br />
10 n = datetime.datetime.now()<br />
11 uhr="%02d:%02d:%02d"%(n.hour,n.m<strong>in</strong>ute,n.second)<br />
12 c = page % {’uhr’: uhr}<br />
13 return Response(c, content_type="text/html")<br />
14 def statisch(request):<br />
15 base = os.path.dirname(__file__)<br />
16 # fuehrendes / bei path wegmachen<br />
17 asset_fname = os.path.jo<strong>in</strong>(base, request.path[1:])<br />
18 asset = file(asset_fname).read()<br />
19 return Response(asset)<br />
E<strong>in</strong>e Python-Funktion je Seite<br />
• Klassisches Dispatch<strong>in</strong>g<br />
• Mappen Anfrage auf Funktion<br />
• Meist auf Basis der URL, geht auch<br />
auf Grund des Zustands<br />
E<strong>in</strong>e Python-Funktion für alles<br />
• E<strong>in</strong> Python-Funktion generiert alles<br />
• Je nach Bedarf spezifisch Content<br />
(durch Funktionsaufrufe) generieren<br />
Komb<strong>in</strong>iert<br />
• application-Objekt von WSGI als “e<strong>in</strong><br />
Skript für alles”, e<strong>in</strong>fache API<br />
• Dar<strong>in</strong> Dispatcher um unterschiedliche<br />
Seiten zu generieren<br />
rout<strong>in</strong>g.py<br />
1 from werkzeug.wrappers import Request, Response<br />
2 product_page = """%(name)s s<strong>in</strong>d toll"""<br />
3 def product(request, name):<br />
4 return product_page % {’name’: name}<br />
5 service_page = """Wir kuemmern uns um %(name)s"""<br />
6 def service(request, name):<br />
7 return service_page % {’name’: name}<br />
8 @Request.application<br />
9 def application(request):<br />
10 name = "ACME Luftgitarren"<br />
11 if request.path.startswith("/product"):<br />
12 c = product(request, name)<br />
13 elif request.path.startswith("/service"):<br />
14 c = service(request, name)<br />
15 else:<br />
16 c = "nicht verfuegbar"<br />
17 return Response(c)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 51 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 52 / 91
Grundlagen Web<br />
Rout<strong>in</strong>g<br />
Grundlagen Web<br />
Rout<strong>in</strong>g<br />
E<strong>in</strong>fache Rout<strong>in</strong>g-Abstraktion<br />
Abbildung URL-Anfang auf Funktion<br />
• Falls URL mit Schlüssel beg<strong>in</strong>nt, dann<br />
auf passende Funktion weiterleiten<br />
• Vorbelegen von Funktionsparametern<br />
möglich, nur e<strong>in</strong> Parameter (request)<br />
erwartet<br />
Mächtigere Alternativen <strong>in</strong> Frameworks<br />
• URL-Matcher – wann passt e<strong>in</strong>e URL<br />
auf e<strong>in</strong> Pattern<br />
• Übernahme von Parametern aus URL<br />
und Typkonvertierung automatisch<br />
• Ausgefeilte Regelwerke und<br />
Konfigurationsmöglichkeiten<br />
• Beispiel: werkzeug<br />
werkzeug.pocoo.org/docs/rout<strong>in</strong>g<br />
(brauchen wir nicht)<br />
1 from werkzeug.wrappers import Request, Response<br />
2 from rout<strong>in</strong>g import product, service<br />
3<br />
4 name = "ACME Luftgitarren"<br />
5 url_map = {<br />
6 ’/product/’: lambda req: product(req, name),<br />
7 ’/service/’: lambda req: service(req, name),<br />
8 # None default<br />
9 None: lambda req: product(req, name)<br />
10 }<br />
11<br />
12<br />
13 @Request.application<br />
14 def application(request):<br />
15 for key <strong>in</strong> url_map:<br />
16 if key and request.path.startswith(key):<br />
17 return Response(url_map[key](request))<br />
18 return Response(url_map[None](request))<br />
H<strong>in</strong>weis: Abbildung sollte e<strong>in</strong>deutig se<strong>in</strong>, sonst<br />
ist Reihenfolge nicht def<strong>in</strong>iert<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 53 / 91<br />
Struktur Programmlogik<br />
H<strong>in</strong>weise<br />
• Trennen HTML und Code, durch<br />
E<strong>in</strong>satz von Templat<strong>in</strong>g erledigt<br />
• E<strong>in</strong>satz von Funktionen und Objekten<br />
für die Programmlogik<br />
• Ausgabe (Generieren HTML) meiden<br />
• Ausnahme für dynamische Strukturen<br />
(Listen, Tabellen) da mächtigere<br />
Templatesprache/Controls fehlen<br />
• Strikte Reihenfolge e<strong>in</strong>halten<br />
Reihenfolge<br />
• Programmzustand wiederherstellen<br />
(Auswertung E<strong>in</strong>gabe, Z13)<br />
• Berechnung neuer Zustand<br />
(Funktionalität Z15–Z18)<br />
• Ausgabe (Templat<strong>in</strong>g und<br />
Ausgaberout<strong>in</strong>en Z20–Z21)<br />
1 from werkzeug.wrappers import Request, Response<br />
2<br />
3 page = """Zaehler<br />
4 %(zahl)d <br />
5 <br />
6 <br />
7 <br />
8 """<br />
9<br />
10 @Request.application<br />
11 def application(request):<br />
12<br />
13 zahl = <strong>in</strong>t(request.form.get("zahl", "0"))<br />
14 if request.form.get("up", None):<br />
15 zahl += 1<br />
16 elif request.form.get("down", None):<br />
17 zahl -= 1<br />
18<br />
19 c = page % {’zahl’ : zahl}<br />
20 return Response(c, content_type="text/html")<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 54 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Lebensdauer Daten über Anfragen h<strong>in</strong>aus<br />
Bisher Daten nur aus der Anfrage<br />
• Ke<strong>in</strong> Zustand <strong>in</strong> der Anwendung<br />
• Daten nur <strong>in</strong> der Anfrage vorhanden<br />
• Eigentlich ke<strong>in</strong> Anwendungskontext<br />
• Erste Ausnahme: Zähler zahl<br />
Anwendungskontext<br />
• Satz von Variable/Wert-Paaren<br />
Gültigkeitsbereiche<br />
• Anwendung: Für alle gleich, für die<br />
Lebensdauer der Anwendung,<br />
Beispiel: Klick-Zähler<br />
• Session: Für jeden Anwender<br />
unterschiedlich, für e<strong>in</strong>e Folge von<br />
Interaktionen e<strong>in</strong>es Anwenders<br />
(Session) gleich, Beispiel: Warenkorb<br />
Lebensdauer Daten<br />
• Block<br />
• Programmiersprachenebene, {}<br />
• Lokale Variablen<br />
• Nicht spezifisch für Web<br />
• Während e<strong>in</strong>er Anfrage<br />
• Für Dauer Bearbeitung e<strong>in</strong>er Anfrage<br />
• In Prog.sprache Attribute des<br />
request- Objekts,<br />
nicht globale Variablen<br />
• Session<br />
• Interaktionsfolge e<strong>in</strong>es Anwenders<br />
• In Prog.sprache nicht abgebildet<br />
• Meist Speicher oder Dateisystem<br />
• Anwendung<br />
• Über Session h<strong>in</strong>aus<br />
• In Prog.sprache nicht abgebildet<br />
• Meist persistent (Dateisystem, DBMS)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 55 / 91<br />
Sessions<br />
Session<br />
• im Mehrbenutzerbetrieb<br />
• mehrere Interaktionen<br />
• je Benutzer<br />
• im gleichen Anwendungskontext<br />
(bestehend aus Daten)<br />
Welche Daten, Beispiele<br />
• Benutzerdaten, E<strong>in</strong>stellungen,<br />
Optionen, E<strong>in</strong>kaufskorb . . .<br />
Problem<br />
• HTTP-Protokoll ist zustandslos,<br />
Request/Response<br />
• Ke<strong>in</strong>e Daten der letzten Anfrage <strong>in</strong><br />
aktueller Anfrage<br />
Lösung – Explizite Session-Verwaltung<br />
Anwendungskontext<br />
• Kontext-Daten manuell verwalten<br />
• Ablage: Client (Browser) oder Server<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 56 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Anwendungskontext im Client<br />
Kontext im Client – Formulare<br />
Anwendungskontext im Web-Browser<br />
• Manuelle Weitergabe des Kontextes<br />
von Seite zu Seite<br />
• Techniken: Formulare (GET/POST),<br />
Cookies<br />
Vorteile<br />
• Ke<strong>in</strong>e Belastung des Servers<br />
(Datenhaltung)<br />
• Ke<strong>in</strong>e e<strong>in</strong>deutige Zuordnung zu<br />
Server-Session notwendig<br />
Nachteile<br />
• Datenübertragung je Anfrage<br />
• Anfällig gegenüber Manipulationen<br />
• Abhängig von Web-Client<br />
• Garantie Persistenz schwierig<br />
Formulare als Träger des<br />
Anwendungskontextes<br />
• Verstecke Formularfelder<br />
<br />
• Alle Schlüssel/Wert-Paare<br />
Weitergabe Anfrage<br />
• POST: Datenstrom <strong>in</strong> Anfrage<br />
• GET: Query-Str<strong>in</strong>g <strong>in</strong> Anfrage<br />
(sichtbar)<br />
Auswertung/Wiederherstellung<br />
• Automatisch: request.form/args<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import cgi<br />
3 _hidden = ’’<br />
5<br />
6 def render_context(context):<br />
7 entries = []<br />
8 for k,v <strong>in</strong> context.iteritems():<br />
9 v = cgi.escape(unicode(v), True)<br />
10 entry = _hidden % {’key’: k, ’value’: v}<br />
11 entries.append(entry)<br />
12 return "\n".jo<strong>in</strong>(entries)<br />
13<br />
14 @Request.application<br />
15 def application(request):<br />
16 ctx, args = {}, request.args<br />
17 ctx[’a’] = args[’a’] if ’a’ <strong>in</strong> args else ’A’<br />
18 ctx[’b’] = args[’b’] if ’b’ <strong>in</strong> args else ’B’<br />
19 ctx[’z’] = <strong>in</strong>t(args[’z’]) if ’z’ <strong>in</strong> args else 0<br />
20 ctx[’z’] += 17<br />
21 f =’%s’<br />
23 c = f % render_context(ctx)<br />
24 for k, v <strong>in</strong> ctx.iteritems():<br />
25 c += "%s: %s\n" % (k, v)<br />
26 return Response(c, content_type="text/html")<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 57 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 58 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Kontext im Client – Generiertes Formular<br />
Cookies<br />
Generiertes Formular<br />
• Verstecke E<strong>in</strong>gabefelder für<br />
Schlüssel/Wert-Paare<br />
• name als Schlüssel<br />
• value als Wert<br />
• Werden automatisch durch Formular<br />
submit übertragen<br />
• Formular selbst hat ke<strong>in</strong>e sichtbaren<br />
Elemente außer dem Knopf<br />
1 <br />
2 <br />
3 <br />
4 <br />
5 <br />
6 <br />
7 a: A<br />
8 b: B<br />
9 z: 17<br />
Ziel – Lokale Datenablage<br />
• Daten im Browser speichern<br />
• Über Neustart des Browsers<br />
• Verwaltet durch Browser<br />
• Server setzt, Browser sendet<br />
automatisch zurück<br />
• Missbrauch, Ad-Track<strong>in</strong>g<br />
Cookies<br />
• Namen: beliebig, max. 300 Cookies<br />
• Wert: max. 4KB/Cookie (<strong>in</strong>kl. Namen)<br />
• Verfallsdatum (Expires): Nach<br />
Verfallsdatum ungültig, wird gelöscht<br />
• Doma<strong>in</strong> (Pfad): Nur an diese Doma<strong>in</strong>,<br />
max. 20 Cookies je Server/Doma<strong>in</strong><br />
• secure: nur wenn gesichert übertragen<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 59 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 60 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Cookies – Übertragung<br />
Cookies setzen und lesen<br />
setcookie.cgi<br />
Übertragung – HTTP, RFC 2109/6265<br />
Manuell – CGI<br />
1 #!/usr/b<strong>in</strong>/python<br />
• Teil des HTTP-Headers<br />
• Set-Cookie: =,<br />
Cookie: =<br />
• PATH=<br />
• Header Set-Cookie<br />
• Auslesen aus Umgebungsvariable<br />
HTTP_COOKIE<br />
• Inhalt s<strong>in</strong>d Schlüssel=Wert Paare<br />
2 from datetime import datetime<br />
3 pr<strong>in</strong>t "Set-Cookie: jetzt="+ str(datetime.now())<br />
4 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
5 import cgi, cgitb<br />
6 cgitb.enable()<br />
7 import os<br />
8 pr<strong>in</strong>t "Cookie is: "+str(os.environ["HTTP_COOKIE"])<br />
• SECURE (optional)<br />
Mit Standardbibliothek<br />
• URL-encodiert (cgi.escape), %FF<br />
• Cookies auf dem Server mit Cookie<br />
Ablauf – Beispiel<br />
• Server schickt Set-Cookie-Header als<br />
Teil der Antwort auf e<strong>in</strong>e Anfrage<br />
• Ab da schickt der Browser immer<br />
wieder das Cookie <strong>in</strong> der Anfrage mit<br />
Cookie-Header<br />
• Mehrere Cookies <strong>in</strong> dem e<strong>in</strong>en<br />
Header-Feld<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 61 / 91<br />
• Cookie.SimpleCookie als e<strong>in</strong>fache<br />
Abstraktion<br />
• Schreiben und Lesen<br />
• Mehrere Schlüsselwert-Paare im<br />
Beispiel<br />
Set-Cookie: jetzt="2013-03-11<br />
19:52:47.505760"; Path=/<br />
• Löschen: Leerer Inhalt und<br />
überschrittenes Verfallsdatum<br />
1 #!/usr/b<strong>in</strong>/python<br />
2 from datetime import datetime<br />
3 import os, cgi, Cookie<br />
4 cookie = Cookie.SimpleCookie()<br />
5 cookie["jetzt"] = str(datetime.now())<br />
6 cookie["jetzt"]["path"] = "/"<br />
7 pr<strong>in</strong>t cookie.output()<br />
8 pr<strong>in</strong>t "Content-Type: text/html\n"<br />
9 rc = Cookie.SimpleCookie(os.environ["HTTP_COOKIE"])<br />
10 pr<strong>in</strong>t "Cookie is: "+rc["jetzt"].value<br />
11 pr<strong>in</strong>t cookie.output()<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 62 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Kontext im Client – Cookies<br />
Kontext im Client – Sichere Cookies<br />
Cookie als Träger des<br />
Anwendungskontextes<br />
• Schlüssel/Wert-Paare <strong>in</strong> Cookies<br />
• Alternativ zusammenpacken als<br />
Query-Str<strong>in</strong>g <strong>in</strong> e<strong>in</strong>em Cookie möglich<br />
Weitergabe zum Browser<br />
• set_cookie<br />
• Automatisch, über Header<br />
Auswertung/Wiederherstellung<br />
• Mit request.cookies<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import cgi, Cookie<br />
3<br />
4 def render_context(content, context):<br />
5 response = Response(content,<br />
6 content_type="text/html")<br />
7 for k,v <strong>in</strong> context.iteritems():<br />
8 v = cgi.escape(unicode(v), True)<br />
9 response.set_cookie(k, v)<br />
10 return response<br />
11<br />
12 @Request.application<br />
13 def application(request):<br />
14 ctx, args = {}, request.args<br />
15 ctx[’a’] = args[’a’] if ’a’ <strong>in</strong> args else ’A’<br />
16 ctx[’b’] = args[’b’] if ’b’ <strong>in</strong> args else ’B’<br />
17 ctx[’z’] = <strong>in</strong>t(args[’z’]) if ’z’ <strong>in</strong> args else 0<br />
18 ctx[’z’] += 17<br />
19 c = []<br />
20 response = render_context(c, ctx)<br />
21 c.append(’’)<br />
23 c.append(’’)<br />
24 for k,v <strong>in</strong> request.cookies.iteritems():<br />
25 c.append("%s: %s\n" % (k, v))<br />
26 return response<br />
Verschlüsseln der Daten auf dem Client<br />
• Schwierig zu lesen, schwierig zu<br />
manipulieren<br />
• E<strong>in</strong>fache Sicherung von Manipulation:<br />
F<strong>in</strong>gerabdruck<br />
Verh<strong>in</strong>dern von Manipulationen<br />
• Wenn Daten verändert wurden, dann<br />
stimmt der “F<strong>in</strong>gerabdruck” der Daten<br />
nicht mehr<br />
• Standardalgorithmus SHA1<br />
• E<strong>in</strong>gabe ist beliebiger Str<strong>in</strong>g (Zahl)<br />
• Ergebnis ist e<strong>in</strong> 160-Bit<br />
Message-Digest (meist als Hex-Str<strong>in</strong>g<br />
dargestellt)<br />
Python und SHA1<br />
• hashlib.sha1(message).hexdigest()<br />
RFC 3174 “ The SHA-1 is called secure<br />
because it is computationally <strong>in</strong>feasible to<br />
f<strong>in</strong>d a message which corresponds to a<br />
given message digest, or to f<strong>in</strong>d two<br />
different messages which produce the<br />
same message digest. Any change to a<br />
message <strong>in</strong> transit will, with very high<br />
probability, result <strong>in</strong> a different message<br />
digest, and the signature will fail to verify.”<br />
Geheimes Salz<br />
• Nachricht muss (teilweise) geheim<br />
se<strong>in</strong>, sonst kann Digest berechnet<br />
werden<br />
• Lösung: H<strong>in</strong>zufügen e<strong>in</strong>es geheimen<br />
“Salzes” (Salt), e<strong>in</strong> beliebiger Str<strong>in</strong>g<br />
a99219f952208c68c30cb4a99cf9d7902e395702<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 63 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 64 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Kontext im Client – Sichere Cookies, Beispiel<br />
Kontext im Client – Sichere Cookies, werkzeug<br />
ctxlib.py<br />
1 import urllib, hashlib, urlparse<br />
2<br />
3 SALT="secret"<br />
4 CONTEXT, FP = "context", "f<strong>in</strong>gerpr<strong>in</strong>t"<br />
5 def render_context(response, context):<br />
6 ctx = urllib.urlencode(context)<br />
7 response.set_cookie(CONTEXT, ctx)<br />
8 fp = hashlib.sha1(ctx+SALT).hexdigest()<br />
9 response.set_cookie(FP, fp)<br />
10<br />
11 def restore_context(request):<br />
12 if CONTEXT not <strong>in</strong> request.cookies:<br />
13 return {}<br />
14 if FP not <strong>in</strong> request.cookies:<br />
15 raise Exception("Security Error")<br />
16 ctx = request.cookies[CONTEXT]<br />
17 fp = hashlib.sha1(ctx+SALT).hexdigest()<br />
18 if fp != request.cookies[FP]:<br />
19 raise Exception("Security Error")<br />
20 ret = urlparse.parse_qs(ctx)<br />
21 for k <strong>in</strong> ret: # nur e<strong>in</strong> Wert<br />
22 ret[k] = "".jo<strong>in</strong>(ret[k])<br />
23 return ret<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import cgi, Cookie, ctxlib<br />
3<br />
4 form = """"""<br />
6<br />
7 @Request.application<br />
8 def application(request):<br />
9 ctx = ctxlib.restore_context(request)<br />
10 if ’a’ not <strong>in</strong> ctx: # un<strong>in</strong>itialisiert<br />
11 ctx[’a’], ctx[’b’] = ’A’, ’B’<br />
12 ctx[’z’] = ’0’<br />
13 c = [form]<br />
14 for k,v <strong>in</strong> ctx.iteritems():<br />
15 c.append("%s: %s\n" % (k, "".jo<strong>in</strong>(v)))<br />
16 ctx[’z’] = <strong>in</strong>t(ctx[’z’]) # Typkonversion<br />
17 ctx[’z’] += 17 # Zustandsfortschritt<br />
18 response = Response(c,content_type="text/html")<br />
19 ctxlib.render_context(response, ctx)<br />
20 return response<br />
SecureCookie <strong>in</strong> werkzeug<br />
• Teil der Erweiterungen<br />
• Verfolgt vorgestelltes Pr<strong>in</strong>zip<br />
• Leeres Dictionary bei Manipulation<br />
ctxlibwerkzeug.py<br />
1 import werkzeug.contrib.securecookie as sc<br />
2<br />
3 SALT="secret"<br />
4 CONTEXT="context"<br />
5 def render_context(response, ctx):<br />
6 scs = sc.SecureCookie(ctx, SALT).serialize()<br />
7 response.set_cookie(CONTEXT, scs)<br />
8<br />
9 def restore_context(request):<br />
10 if CONTEXT not <strong>in</strong> request.cookies:<br />
11 return {}<br />
12 scs = request.cookies[CONTEXT]<br />
13 ctx = sc.SecureCookie.unserialize(scs, SALT)<br />
14 if not ctx:<br />
15 raise Exception("Security Error")<br />
16 return ctx<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import cgi, Cookie, ctxlibwerkzeug as ctxlib<br />
3<br />
4 form = """"""<br />
6<br />
7 @Request.application<br />
8 def application(request):<br />
9 ctx = ctxlib.restore_context(request)<br />
10 if ’a’ not <strong>in</strong> ctx: # un<strong>in</strong>itialisiert<br />
11 ctx[’a’], ctx[’b’] = ’A’, ’B’<br />
12 ctx[’z’] = 0<br />
13 c = [form]<br />
14 for k,v <strong>in</strong> ctx.iteritems(): # Typ bleibt <strong>in</strong>t<br />
15 c.append("%s:%s\n"%(k,"".jo<strong>in</strong>(str(v))))<br />
16 # ke<strong>in</strong>e Typkonversion mehr notwendig<br />
17 ctx[’z’] += 17 # Zustandsfortschritt<br />
18 response = Response(c,content_type="text/html")<br />
19 ctxlib.render_context(response, ctx)<br />
20 return response<br />
• Serialisierung nicht als Querystr<strong>in</strong>g,<br />
default pickle<br />
• Typ<strong>in</strong>formation bleibt erhalten<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 65 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 66 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Anwendungskontext im Server<br />
Session-Identifikation im Web-Browser<br />
Verwalten des Anwendungskontextes <strong>in</strong><br />
der Web-Anwendung im Server<br />
• E<strong>in</strong> Datensaz je Session<br />
• Session-Identifikation über Browser<br />
notwendig<br />
Vorteile<br />
• HTTP ist zustandslos<br />
• Unabhängig(er) von Client<br />
• Schwieriger zu manipulieren<br />
• Persistenz<br />
Nachteile<br />
• Hohe Belastung des Servers<br />
• Session-Zuordnung trotzdem<br />
notwendig im Browser<br />
• Session-Id<br />
Session-ID<br />
Anwendungskontext<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 67 / 91<br />
Session-Identifikation (Session-ID)<br />
• E<strong>in</strong> e<strong>in</strong>e<strong>in</strong>deutiger Bezeichner je<br />
Session<br />
• Nicht vorhersagbar<br />
• IP-Adresse, Benutzerkennung und Zeit<br />
weder ausreichend noch h<strong>in</strong>reichend<br />
• Internet-Café, anonymer<br />
Mehrbenutzerbetrieb<br />
• Dynamisches Ändern der IP-Adresse<br />
• Meist Hash e<strong>in</strong>er Zufallszahl plus<br />
IP-Adresse (99,9...%-Lösung)<br />
Verwalten der Session-ID<br />
• Bevorzugt im Cookie<br />
• Oft optional im GET-Str<strong>in</strong>g<br />
• Falls Cookie verboten<br />
• Navigation mit location: geht schief<br />
(und ist verboten!)<br />
Session-ID <strong>in</strong> Python erzeugen<br />
• import uuid, OpenSSL<br />
r = OpenSSL.rand.bytes(16)<br />
sessionid = uuid.UUID(bytes=r)<br />
#38e9aa97-fe49-2ea2-4d92-4aa0369942a0<br />
• “Gute” Zufallszahl<br />
• Ausreichend e<strong>in</strong>deutig<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 68 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Session-Verwaltung auf Server<br />
Session-Verwaltung auf Server mit Python<br />
Zusammenspiel Session-ID und Kontext<br />
Anwendungskontext Verwendung<br />
• Inhalte des Anwendungskontextes<br />
werden auf Server gehalten<br />
• Assoziation von Session-ID mit<br />
Anwendungskontext<br />
• Session-Objekte, Middleware<br />
• Middleware entscheidet über Ort und<br />
Art der Speicherung<br />
Session-ID<br />
Datenhaltung auf Server, Alternativen<br />
• Hauptspeicher/Prozesskontext: Bei<br />
CGI/WSGI nicht (e<strong>in</strong>fach) möglich,<br />
Zuordnung Anfrage auf Prozess bei<br />
CGI immer neu oder durch<br />
Web-Server gesteuert<br />
• Datei: Aufwändig, viele kle<strong>in</strong>e Dateien<br />
schreiben, Session-ID ist Date<strong>in</strong>ame<br />
• Datenbank: Sicher, aufwändig,<br />
unabhängig von Web-Server<br />
Web-Server/Skriptumgebung<br />
Server/Host<br />
• Bevorzugt mit sicheren Cookies<br />
Ablage zwischen den Seitenaufrufen<br />
• Prozesskontext des Skripts hat ke<strong>in</strong>e<br />
Bedeutung! Je Anfrage andere Python<br />
Instanz möglich oder (CGI) immer neu<br />
• Serialisierung meist mit pickle,<br />
beliebige Python-Objekte möglich<br />
• Ablageort meist Dateisystem und<br />
Datenbank; Client (Cookie) oder<br />
Hauptspeicher (Shared Memory,<br />
memcached) möglich<br />
Shared<br />
Memory<br />
Hauptspeicher/<br />
Prozesskontext<br />
Dateisystem<br />
Datenbank<br />
Dateisystem<br />
Datenbank<br />
Web-Server/Skriptumgebung<br />
Server/Host<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 69 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 70 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Session-Verwaltung auf Server – Session-ID<br />
Session-ID<br />
• Wenn schon vorhanden, dann e<strong>in</strong>fach<br />
als Str<strong>in</strong>g zurückgeben<br />
• Wenn nicht, dann vorher erzeugen<br />
• Session-ID <strong>in</strong> e<strong>in</strong>em Cookie<br />
• Header setzen beim Erzeugen<br />
• Antwort muss also schon zur<br />
Verfügung stehen<br />
• Holen und gegebenenfalls erzeugen<br />
Verwendung<br />
• Vor jeder Anfrage: Inhalte auslesen<br />
• Nach jeder Anfrage: Inhalte speichern<br />
• Ablageort beliebig: Schlüssel (zum<br />
Beispiel Date<strong>in</strong>ame) ist die Session-ID<br />
session_id<br />
1 import os, uuid, OpenSSL<br />
2 import werkzeug.contrib.securecookie as sc<br />
3<br />
4 SESCOOKIE, SALT = "session_cookie", "secure"<br />
5<br />
6 def get_sid(request, response):<br />
7 "session_id aktueller session, ggf. neu"<br />
8 if SESCOOKIE not <strong>in</strong> request.cookies: # new<br />
9 r = OpenSSL.rand.bytes(16)<br />
10 sid = str(uuid.UUID(bytes=r))<br />
11 cd = {SESCOOKIE: sid}<br />
12 c = sc.SecureCookie(cd, SALT).serialize()<br />
13 response.set_cookie(SESCOOKIE, c)<br />
14 else:<br />
15 c = request.cookies[SESCOOKIE]<br />
16 cd = sc.SecureCookie.unserialize(c, SALT)<br />
17 sid = cd[SESCOOKIE]<br />
18 return sid<br />
Session-Verwaltung auf Server – Dateisystem<br />
Sessions selbst gemacht (Verständnis)<br />
• Session-Objekt e<strong>in</strong>faches Dictionary<br />
mit E<strong>in</strong>trag ’_SID’ auf Session-ID<br />
• Ablage <strong>in</strong> Dateisystem<br />
• Ke<strong>in</strong>e Überprüfung ob Kollision<br />
(schon vorhandene Session)<br />
• Ke<strong>in</strong> Aufräumen alter Sessions<br />
Verwendung, als Middleware<br />
• response erzeugen, Session-Objekt<br />
mit load_start erhalten<br />
• Die Anwendung manipuliert das<br />
Session-Objekt (nicht _ * )<br />
• save_session am Ende zum Speichern<br />
aktualisierter Session Inhalte<br />
• kill_session Session technisch<br />
beenden (vermeiden)<br />
session_fs<br />
1 import os, cPickle<br />
2<br />
3 STORAGE, SID = "/tmp", "_SID"<br />
4 def load_session(session_id):<br />
5 fpath = os.path.jo<strong>in</strong>(STORAGE, session_id)<br />
6 try:<br />
7 return cPickle.load(file(fpath, "r"))<br />
8 except (IOError, EOFError) as e:<br />
9 return {SID: session_id}<br />
10<br />
11 def save_session(session):<br />
12 fpath = os.path.jo<strong>in</strong>(STORAGE, session[SID])<br />
13 cPickle.dump(session, file(fpath, "w"))<br />
14<br />
15 def kill_session(session):<br />
16 fpath = os.path.jo<strong>in</strong>(STORAGE, session[SID])<br />
17 try:<br />
18 os.unl<strong>in</strong>k(fpath)<br />
19 except OSError:<br />
20 pass<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 71 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 72 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Session-Verwaltung auf Server – Beispiel<br />
Achtung beim Schreiben – Nebenläufigkeit!<br />
Anforderung Anwendung<br />
• Hochzählen Zähler je Anfrage, Start<br />
17, je Benutzer unabhängig<br />
Umsetzung, wie beschrieben<br />
• Anwendung selbst <strong>in</strong> doit ausgelagert<br />
• Sessionverwaltung ist Middleware<br />
1 from werkzeug.wrappers import Request, Response<br />
2 from session_id import get_sid<br />
3 from session_fs import load_session, save_session<br />
4<br />
5 page = file("page.tpl").read()<br />
6<br />
7 def doit(request, session):<br />
8 if "zahl" not <strong>in</strong> session:<br />
9 session["zahl"] = 16<br />
10 session["zahl"] += 1<br />
11 lis = []<br />
12 for k,v <strong>in</strong> session.iteritems():<br />
13 if not k.startswith("_"):<br />
14 lis.append("%s = %s" % (str(k), str(v)))<br />
15 return "\n".jo<strong>in</strong>(lis)<br />
16<br />
17 @Request.application<br />
18 def application(request):<br />
19 c = []<br />
20 response = Response(c, content_type="text/html")<br />
21 sid = get_sid(request, response)<br />
22 session = load_session(sid)<br />
23 content = doit(request, session)<br />
24 save_session(session)<br />
25 dic = {’title’:’Sessions’, ’content’: content}<br />
26 c.append(page % dic)<br />
27 return response<br />
Parallele Zugriffe<br />
• Dateien liegen auf e<strong>in</strong>em Server<br />
• Mehrere Zugriffe auf die Datei zur<br />
selben Zeit ist möglich<br />
• Selbst bei e<strong>in</strong>er Session-Datei, zum<br />
Beispiel bei (böse)<br />
• Lesender/schreibender Zugriff<br />
Konflikte – Unklares Verhalten<br />
• Gleichzeitiges Schreiben und Lesen<br />
• Was wird gelesen? Halb/halb?<br />
• Was wird geschrieben? Noch<br />
konsistent?<br />
Lösung – Sperren<br />
• Verwaltung exklusiver Zugriffsrechte<br />
Web-<br />
Server<br />
Dateisystem<br />
Server/Host<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 73 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 74 / 91<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Sperren mit Dateien<br />
Exklusiver Zugriff auf Datei, fnctl<br />
• flock(fd, operation) für<br />
Sperrverwaltung, nur Unix<br />
• fd File descriptor, offene Datei<br />
• Konstanten LOCK_ * <strong>in</strong> fnctl<br />
Operationen (Auszug)<br />
• LOCK_EX (2): Exklusiver Zugriff, warten<br />
wenn (e<strong>in</strong>e) Sperre noch vergeben<br />
• LOCK_UN (8): Sperre freigeben<br />
Verwendung<br />
• Klammer um Anfang/Ende der<br />
Anwendung (kompletter Request ist<br />
kritischer Bereich aus Session-Sicht)<br />
• lockf, thread-sicher Sperre erhalten<br />
• _ * -Schlüssel, <strong>in</strong>terne Merker<br />
• ToDo: Schließen im Fehlerfall, . . .<br />
session_fslock<br />
1 import os, cPickle, fcntl<br />
2 from session_fs import STORAGE, SID, kill_session<br />
3<br />
4 FKEY, LOCKFKEY = "_file", "_lockfile"<br />
5 def load_session(session_id):<br />
6 fpath = os.path.jo<strong>in</strong>(STORAGE, session_id)<br />
7 lockf = file(fpath, "a") # force open file<br />
8 fcntl.flock(lockf, fcntl.LOCK_EX)<br />
9 f = file(fpath, "r+") # lock to read and write<br />
10 try:<br />
11 session = cPickle.load(f)<br />
12 except EOFError:<br />
13 session = {}<br />
14 session[SID] = session_id<br />
15 session[LOCKFKEY] = lockf<br />
16 session[FKEY] = f<br />
17 return session<br />
18<br />
19 def save_session(session):<br />
20 f = session[FKEY]; del session[FKEY]<br />
21 lockf = session[LOCKFKEY]; del session[LOCKFKEY]<br />
22 f.seek(0) # Anfang von Datei<br />
23 cPickle.dump(session, f)<br />
24 f.close()<br />
25 fcntl.flock(lockf, fcntl.LOCK_UN)<br />
26 lockf.close()<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 75 / 91<br />
Sessions mit werkzeug<br />
Realisieren von Sessions<br />
• Komplexe, fehleranfällige Middleware<br />
• Vorgefertigte Lösungen verwenden<br />
• Beispiel: werkzeug.contrib.sessions<br />
werkzeug.contrib.sessions<br />
• Vorgefertigte Bibliothek, kümmert sich<br />
um Cookies und Storage<br />
• E<strong>in</strong>e Option ist Dateisystem zum<br />
speichern (Z23)<br />
• Als Middleware e<strong>in</strong>fach zwischen<br />
Aufruf und Ergebnis setzen<br />
ToDo<br />
• Aufräumen abgelaufener Sessions<br />
• Weitere Konfiguration möglich<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import werkzeug.contrib.sessions as sessions<br />
3<br />
4 page = file("page.tpl").read()<br />
5<br />
6 def doit(request, session):<br />
7 if "zahl" not <strong>in</strong> session:<br />
8 session["zahl"] = 16<br />
9 session["zahl"] += 1<br />
10 lis = []<br />
11 for k,v <strong>in</strong> session.iteritems():<br />
12 lis.append("%s = %s" % (str(k), str(v)))<br />
13 return "\n".jo<strong>in</strong>(lis)<br />
14<br />
15 @Request.application<br />
16 def app(request):<br />
17 session = request.environ["werkzeug.session"]<br />
18 content = doit(request, session)<br />
19 dic = {’title’:’Sessions’, ’content’: content}<br />
20 return Response(page%dic,content_type="text/html")<br />
21<br />
22 # haenge die Middleware dazwischen<br />
23 fsstore=sessions.FilesystemSessionStore()<br />
24 application=sessions.SessionMiddleware(app,fsstore)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 76 / 91
Grundlagen Web<br />
Zustandsverwaltung über Anfrage h<strong>in</strong>aus<br />
Grundlagen Web<br />
Bibliotheken<br />
Sessions<br />
Integration von Datenbanken<br />
Sessions – Umsetzung<br />
Datenbanken für Web-Anwendungen<br />
• Serialisieren häufig notwendig<br />
• Mehrbenutzeranw., immer mit dabei<br />
• Versuchen wenig Daten <strong>in</strong> der Session zu halten<br />
• Ke<strong>in</strong>e Frames, ke<strong>in</strong> , konkurrierende Zugriffe<br />
Dateien als Ablage und sperren häufig nicht beste Wahl<br />
• Funktioniert nur gut auf e<strong>in</strong>em Dateisystem auf e<strong>in</strong>em Rechner (/tmp), nicht gut<br />
bei Network File System (NFS)<br />
• Skaliert nicht immer wie gewünscht, nicht ursprünglich gebaut für sehr viele<br />
Anfrage/Dateien gleichzeitig<br />
• E<strong>in</strong>satzgebiete: Sessions, Zugriffszähler, Kommentare <strong>in</strong> Blogs, . . .<br />
Alternativen für Speicher-Backend<br />
• tmpfs, shared memory mit lock<strong>in</strong>g<br />
• Datenbanken<br />
• Sichere Ablage von Daten<br />
• Design für gleichzeitigen schreibenden und lesenden Zugriff<br />
• Ausgefeilte Sperrverwaltung, Transaktionskonzept (ACID)<br />
• Alle Anwendungsdaten, auch<br />
Session-Daten möglich<br />
Datenbanken und Python<br />
• Anb<strong>in</strong>dung aller wichtigen DBMS<br />
• Python DB-API 2.0 (PEP 249) def<strong>in</strong>iert<br />
DB-unabhängige Schicht (Pythons<br />
JDBC)<br />
• Treiber für spezifische DBMS<br />
Postgresql und Python<br />
• Treiber psycopg2, pygresql<br />
• Postgresql wie gewohnt (auch auf<br />
ZuHause-Image)<br />
Frameworks: Tools, ORM, . . .<br />
Datenbank<br />
Python DB-API 2.0<br />
psycopg<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 77 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 78 / 91<br />
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
Web-Seite mit Python und Postgresql – Beispiel<br />
Verwendung, wie gewohnt<br />
• Verb<strong>in</strong>dungsaufbau connect<br />
• cursor-Konzept, je Verb<strong>in</strong>dung<br />
• Anfragen mit execute, Parameter<br />
verwenden!<br />
• www.python.org/dev/<br />
peps/pep-0249/<br />
• <strong>in</strong>itd.org/psycopg/<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import psycopg2 as pg<br />
3<br />
4 page = file("page.tpl").read()<br />
5 keys = ["vorname", "name", "matnr"]<br />
6 tl<strong>in</strong>e = " %s "<br />
7 thead, tdata = "%s ", "%s "<br />
8<br />
9 def doit(request):<br />
10 conn = pg.connect(host="localhost",<br />
11 user="medien<strong>in</strong>f", password="medien<strong>in</strong>f")<br />
12 curs = conn.cursor()<br />
13 curs.execute("SELECT "+", ".jo<strong>in</strong>(keys)+" FROM studi")<br />
14 lis = [tl<strong>in</strong>e%"".jo<strong>in</strong>(map(lambda k:thead%k,keys))]<br />
15 for row <strong>in</strong> curs.fetchall():<br />
16 lis.append(tl<strong>in</strong>e%"".jo<strong>in</strong>(map(lambda k:tdata%k,row)))<br />
17 return "\n" + "\n".jo<strong>in</strong>(lis) + "\n"<br />
18<br />
19 @Request.application<br />
20 def application(request):<br />
21 content = doit(request)<br />
22 dic = {’title’:’Namen’, ’content’: content}<br />
23 return Response(page%dic,content_type="text/html")<br />
Sicheres Parametrisieren von Anfragen<br />
Externe Daten s<strong>in</strong>d bedrohlich<br />
• Benutzere<strong>in</strong>gabe, Datei, DB, . . .<br />
• Verh<strong>in</strong>dere SQL-Injection<br />
Umsetzung mit execute<br />
• Wie %-Operator, extra Argument<br />
• Tupel oder Dictionary<br />
1 import psycopg2 as pg<br />
2<br />
3 keys = ["vorname", "name", "matnr"]<br />
4 tl<strong>in</strong>e = " %s "<br />
5 thead, tdata = "%s ", "%s "<br />
6<br />
7 def doit(request):<br />
8 conn = pg.connect(host="localhost",<br />
9 user="medien<strong>in</strong>f", password="medien<strong>in</strong>f")<br />
10 curs = conn.cursor()<br />
11 sql = "SELECT "+", ".jo<strong>in</strong>(keys)+" FROM studi "<br />
12 if "matnr" <strong>in</strong> request.args:<br />
13 sql += "WHERE matnr = %s"<br />
14 curs.execute(sql, (request.args["matnr"], ))<br />
15 elif "vorname" <strong>in</strong> request.args:<br />
16 sql += "WHERE vorname LIKE %(vorname)s"<br />
17 like = "%%" + request.args["vorname"] + "%%"<br />
18 curs.execute(sql, {’vorname’: like})<br />
19 else:<br />
20 curs.execute(sql)<br />
21 lis = [tl<strong>in</strong>e%"".jo<strong>in</strong>(map(lambda k:thead%k,keys))]<br />
22 for row <strong>in</strong> curs.fetchall():<br />
23 lis.append(tl<strong>in</strong>e%"".jo<strong>in</strong>(map(lambda k:tdata%k,row)))<br />
24 return "\n"+"\n".jo<strong>in</strong>(lis)+"\n"<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 79 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 80 / 91
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
Sicheres Parameterisieren von Anfragen – Ernst geme<strong>in</strong>t<br />
Postgresql und Python – H<strong>in</strong>weise<br />
<strong>in</strong>itd.org/psycopg/docs/usage.html<br />
Spalten-/Tabellennamen<br />
• Schlüssel vorname, name, matnr s<strong>in</strong>d ke<strong>in</strong>e Variablen, kommen im Quelltext vor<br />
• Falls Spalten-/Tabellennamen dynamisch gesetzt werden, dann müssen diese per<br />
Str<strong>in</strong>g-Operation <strong>in</strong>tegriert werden<br />
• Dann manuell prüfen zum Beispiel mit Dictionary, wobei Schlüssel die<br />
Benutzere<strong>in</strong>gabe ist und der Wert im Quelltext steht<br />
Ergebnisse als Dictionary<br />
• Spaltenname statt Position <strong>in</strong><br />
Anfrage für Wertzugriff<br />
• Verständlicherer Quellcode<br />
• psycopg2.extras.DictCursor<br />
Thread-Sicherheit<br />
• Sowohl Datenbankverb<strong>in</strong>dung<br />
als auch Cursor ist thread-sicher<br />
• Connection-Pools separat<br />
• Praktikum: e<strong>in</strong>e Verb<strong>in</strong>dung conn<br />
je Anfrage<br />
Transaktionen<br />
• E<strong>in</strong>e Transaktion je Verb<strong>in</strong>dung<br />
• Start automatisch, beenden mit<br />
commit oder rollback<br />
>>> import psycopg2.extras as ext<br />
>>> curs=conn.cursor(cursor_factory=ext.DictCursor)<br />
>>> curs.execute("SELECT * FROM studi")<br />
>>> row = curs.fetchone()<br />
>>> row<br />
[815, ’Susi’, ’S<strong>in</strong>nlos’]<br />
>>> row["vorname"]<br />
’Susi’<br />
>>> curs = conn.cursor()<br />
>>> curs.execute("INSERT INTO studi (matnr,vorname,name)"+<br />
" VALUES (777,’Votan’,’Wahnwitz’)")<br />
>>> conn.rollback()<br />
>>> curs.execute("SELECT vorname FROM studi WHERE matnr=777")<br />
>>> curs.fetchall()<br />
[]<br />
>>> curs.execute("INSERT INTO studi (matnr,vorname,name)"+<br />
" VALUES (777,’Votan’,’Wahnwitz’)")<br />
>>> conn.commit()<br />
>>> curs.execute("SELECT vorname FROM studi WHERE matnr=777")<br />
>>> curs.fetchall()<br />
[(’Votan’,)]<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 81 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 82 / 91<br />
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
Web-Anwendungen und Transaktionen<br />
Transaktion – DB-Sicht<br />
• Alles zwischen Beg<strong>in</strong> (auto) und Ende<br />
• DBMS kümmert sich um transaktionale Sicherung<br />
Transaktion – Anwendersicht, Logical Unit of Work<br />
• Formular-Feld gesehen, geändert, gespeichert<br />
• Formular-Feld darf sich zwischenzeitlich nicht geändert haben<br />
• M<strong>in</strong>destens zwei! Anfragen: Lesen <strong>in</strong> erster (Formular aufbauen), Schreiben <strong>in</strong><br />
zweiter (Formular auswerten)<br />
• Mit e<strong>in</strong>er Transaktion alle<strong>in</strong>e nicht durchführbar<br />
Aufgabe: Transaktional sicher arbeiten<br />
• Lesen <strong>in</strong> erster Anfrage, sichern Werte <strong>in</strong> Session<br />
• Vor Schreiben Kontroll-Lesen <strong>in</strong> zweiter Anfrage, Abbruch bei Änderung<br />
• Kontrolllesen <strong>in</strong>nerhalb Transaktion<br />
H<strong>in</strong>weis<br />
• FOR UPDATE nicht vergessen<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 83 / 91<br />
Hochladen von Dateien<br />
Spezielles HTML-Formular<br />
• enctype="multipart/form-data",<br />
POST,
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
Speichern B<strong>in</strong>ärdatei <strong>in</strong> Datenbank<br />
Laden B<strong>in</strong>ärdatei von Datenbank<br />
Passende Datenbank-Struktur<br />
• Tabelle mit Referenz<strong>in</strong>formation<br />
• id, zum Wiederf<strong>in</strong>den<br />
• name, mime, size s<strong>in</strong>d Extras<br />
• Datei als BLOB (B<strong>in</strong>ary Large Object)<br />
mit lobject) <strong>in</strong> Datenbank ablegen<br />
CREATE TABLE b<strong>in</strong> (<br />
id OID PRIMARY KEY,<br />
name TEXT, mime TEXT, size INT<br />
);<br />
...<br />
medien<strong>in</strong>f=> SELECT * FROM b<strong>in</strong>;<br />
id | name | mime | size<br />
-------+-----------------+-----------+-------<br />
31693 | fileupload1.png | image/png | 22595<br />
31694 | fileupload2.png | image/png | 29164<br />
medien<strong>in</strong>f=> \lo_list<br />
Large objects<br />
ID | Owner | Description<br />
-------+-----------+-------------<br />
31693 | medien<strong>in</strong>f |<br />
31694 | medien<strong>in</strong>f |<br />
1 import psycopg2 as pg<br />
2<br />
3 KEY="b<strong>in</strong>arydata"<br />
4 def doit(request): # wenn POST<br />
5 fs = request.files[KEY]<br />
6 blob = fs.stream.read()<br />
7 conn = pg.connect(host="localhost",<br />
8 user="medien<strong>in</strong>f", password="medien<strong>in</strong>f")<br />
9 lo = conn.lobject(0, "wb")<br />
10 lo.write(blob)<br />
11 sql = "INSERT INTO b<strong>in</strong> VALUES "<br />
12 sql += "(%(loid)s, %(name)s, %(type)s, %(size)s)"<br />
13 data = {’loid’: lo.oid, ’name’: fs.filename,<br />
14 ’type’: fs.content_type, ’size’: len(blob)}<br />
15 curs = conn.cursor()<br />
16 curs.execute(sql, data)<br />
17 conn.commit()<br />
18 return "uploaded " + str(lo.oid)<br />
Hochladen mit POST<br />
• Anlegen large object, id<br />
• E<strong>in</strong>tragen Meta-Information <strong>in</strong> Tabelle<br />
• Muss <strong>in</strong> Transaktion se<strong>in</strong><br />
Download Datei von DBMS<br />
• Verwaltungs<strong>in</strong>formationen anhand von<br />
id lesen, zum Beispiel aus<br />
Query-Parameter id<br />
• Lesen des BLOB mit lobject<br />
• Rückgabe von Inhalt der kompletten<br />
Datei mit passenden mimetype zur<br />
Ausgabe<br />
1 from werkzeug.wrappers import Request, Response<br />
2 from dbdownloaddoit import doit<br />
3 page = file("page.tpl").read()<br />
4 img = ’’<br />
5 @Request.application<br />
6 def application(request):<br />
7 if "id" <strong>in</strong> request.args: # das Bid selbst<br />
8 c,mime = doit(<strong>in</strong>t(request.args["id"]))<br />
9 return Response(c, content_type=mime)<br />
10 else: # Rahmen fuer e<strong>in</strong> Beispielbild<br />
11 c = img % {’id’: 31693}<br />
12 dic={’title’:’Image Show’,’content’:c}<br />
13 return Response(page%dic,content_type="text/html")<br />
1 import psycopg2 as pg<br />
2<br />
3 def doit(id): # get image<br />
4 conn = pg.connect(host="localhost",<br />
5 user="medien<strong>in</strong>f", password="medien<strong>in</strong>f")<br />
6 sql = "SELECT mime FROM b<strong>in</strong> WHERE id = %s"<br />
7 curs = conn.cursor()<br />
8 curs.execute(sql, (id, ))<br />
9 res = curs.fetchone()<br />
10 if not res:<br />
11 return "Ke<strong>in</strong> Image", "text/pla<strong>in</strong>"<br />
12 lo = conn.lobject(id, "rb")<br />
13 blob = lo.read()<br />
14 return blob, res[0]<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 85 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 86 / 91<br />
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
Bilder erzeugen mit Image<br />
PDF erzeugen mit reportlab<br />
Alle bekannten Bibliotheken verwendbar<br />
• Beispiel Image<br />
• Zeichnen mit ImageDraw,<br />
• Fonts mit ImageFont<br />
Anzeige e<strong>in</strong>es selbstgebauten Buttons<br />
• Laden e<strong>in</strong>es Templates button.png<br />
• Füllen des Templates mit Text, Text <strong>in</strong><br />
spezifischem Font (Vera) und Größe<br />
(16)<br />
• Ausgabe <strong>in</strong> Str<strong>in</strong>g über cStr<strong>in</strong>gIO da<br />
nur save möglich<br />
Beispiel<br />
• button.png<br />
• http://localhost:8080<br />
• http:.../?text=HS+Rhe<strong>in</strong>Ma<strong>in</strong><br />
1 from werkzeug.wrappers import Request, Response<br />
2 import Image, ImageDraw, ImageFont, cStr<strong>in</strong>gIO<br />
3 fp = "/usr/share/fonts/truetype/"<br />
4 fp += "ttf-bitstream-vera/Vera.ttf"<br />
5 fontsize = 16<br />
6 @Request.application<br />
7 def application(request):<br />
8 text = "Button"<br />
9 if "text" <strong>in</strong> request.args:<br />
10 text = request.args["text"]<br />
11 im = Image.open("button.png")<br />
12 draw = ImageDraw.Draw(im)<br />
13 font = ImageFont.truetype(fp, fontsize)<br />
14 twidth, theight = font.getsize(text)<br />
15 imwidth, imheight = im.size<br />
16 x, y = imwidth/2-twidth/2, imheight/2-(theight/2)<br />
17 draw.text((x, y), text, font=font, fill="black")<br />
18 output = cStr<strong>in</strong>gIO.Str<strong>in</strong>gIO()<br />
19 im.save(output, format="PNG")<br />
20 content = output.getvalue()<br />
21 return Response(content,content_type="image/png")<br />
reportlab<br />
• Bibliothek (frei) zum Erzeugen von<br />
PDF<br />
• Auch Formulare und Textsatz möglich<br />
• E<strong>in</strong>faches Beispiel mit absoluter<br />
Positionierung (auf Canvas zeichnen),<br />
Quadratzahlen<br />
1 from werkzeug.wrappers import Request, Response<br />
2 import cStr<strong>in</strong>gIO<br />
3 from reportlab.pdfgen import canvas<br />
4<br />
5 @Request.application<br />
6 def application(request):<br />
7 output = cStr<strong>in</strong>gIO.Str<strong>in</strong>gIO()<br />
8 c = canvas.Canvas(output)<br />
9 i = 0<br />
10 for x <strong>in</strong> range(50, 500, 85):<br />
11 c.drawStr<strong>in</strong>g(x, 810, "Zahl")<br />
12 c.drawStr<strong>in</strong>g(x+33, 810, "Quadrat")<br />
13 for y <strong>in</strong> range(50, 800, 15):<br />
14 c.drawStr<strong>in</strong>g(x, 842-y, "%d"%i)<br />
15 c.drawStr<strong>in</strong>g(x+33, 842-y, "%d"%(i*i))<br />
16 i += 1<br />
17 c.save()<br />
18 content= output.getvalue()<br />
19 mime = "application/pdf"<br />
20 return Response(content,content_type=mime)<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 87 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 88 / 91
Grundlagen Web<br />
Bibliotheken<br />
Grundlagen Web<br />
Bibliotheken<br />
XML verarbeiten mit lxml<br />
Frameworks – Beispiel django<br />
lxml<br />
• etree, Elementree, e<strong>in</strong>faches DOM<br />
• E<strong>in</strong>facher Zugriff auf Unterelemente<br />
mit f<strong>in</strong>d, auf Attribute mit get<br />
• Viel mehr möglich<br />
1 from werkzeug.wrappers import Request, Response<br />
2 from lxml import etree<br />
3<br />
4 @Request.application<br />
5 def application(request):<br />
6 xmlstr<strong>in</strong>g = file("adressen.xml").read()<br />
7 addrs = etree.fromstr<strong>in</strong>g(xmlstr<strong>in</strong>g)<br />
8 l = []<br />
9 for addr <strong>in</strong> addrs:<br />
10 anrede = addr.f<strong>in</strong>d("anrede").get("bez")<br />
11 name = addr.f<strong>in</strong>d("name").text<br />
12 l.append("%s %s" % (anrede, name))<br />
13 c = "\n".jo<strong>in</strong>(l)<br />
14 return Response(c,content_type="text/html")<br />
<br />
<br />
<br />
<br />
<br />
Hanna Mustermann<br />
Seestr. 5<br />
20000<br />
Hamburg<br />
<br />
<br />
<br />
Demo<br />
3042134<br />
10000<br />
Berl<strong>in</strong><br />
<br />
<br />
django – Das Fullstack Framework<br />
• Alles dabei was man braucht<br />
• Objekt-Relationaler Mapper<br />
• Adm<strong>in</strong>-Oberfläche<br />
• Aussagekräftige URLs, Rout<strong>in</strong>g<br />
• Templat<strong>in</strong>g<br />
• Cach<strong>in</strong>g<br />
• Internationalisierung<br />
• . . .<br />
• Weit verbreitet<br />
• Man ist sehr schnell sehr produktiv!<br />
Alternativen<br />
• wiki.python.org/mo<strong>in</strong>/WebFrameworks, Dutzende<br />
<strong>in</strong> Python<br />
• Andere Sprachen: Ruby (on Rails), PHP (Zend), Java<br />
(JavaEE, JSF), C (-)<br />
Django is a high-level<br />
Python Web framework<br />
that encourages rapid<br />
development and clean,<br />
pragmatic design.<br />
www.djangoproject.com<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 89 / 91<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 90 / 91<br />
Grundlagen Web<br />
Bibliotheken<br />
Fazit – Grundlagen Web<br />
Was passiert wirklich (<strong>in</strong> Frameworks)<br />
• HTTP, CGI/WSGI, Request/Reponse-Zyklus<br />
• Templat<strong>in</strong>g, Rout<strong>in</strong>g<br />
• Sessions, Cookies<br />
• Zustandsmanagement (Anwendungskontext) auf Client und Server<br />
• E<strong>in</strong>e konkrete Middleware (werkzeug) aber noch ke<strong>in</strong> Framework<br />
• Integration von Datenbanken und anderer Bibliotheken<br />
Verwendung<br />
• Immer, wenn auch versteckt <strong>in</strong> Frameworks<br />
• Immer, wenn man durchgreifen will oder muss<br />
• Immer, wenn man bei der Fehlersuche verstehen will was passiert<br />
Als nächstes<br />
• E<strong>in</strong> Framework (nicht django :-) und e<strong>in</strong> Conta<strong>in</strong>er<br />
• JavaEE Web Profile<br />
Prof. Dr. Peter Barth (HS-Rhe<strong>in</strong>Ma<strong>in</strong>) Web-basierte Anwendungen 24. April 2013 91 / 91