31.10.2013 Aufrufe

4 in 1 - Medieninformatik - Hochschule RheinMain

4 in 1 - Medieninformatik - Hochschule RheinMain

4 in 1 - Medieninformatik - Hochschule RheinMain

MEHR ANZEIGEN
WENIGER ANZEIGEN

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 />

&lt;tag&gt; <br />

&lt;b&gt; bold &lt;/b&gt; <br />

a &quot; 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

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!