3.8.2. WiTTFind-Web¶
This repository contains all exercises for DHd2020 Paderborn.
3.8.2.1. Themen¶
In diesem Teil des Workshops wird die Verbindung zwischen Client (Webseite im Browser) und einem Server (Pythonanwendung: Flask) beschrieben.
3.8.2.1.1. Typische Frameworks für Python Services¶
Alle der folgenden Frameworks haben eine Gemeinsamkeit. Es werden grundlegende
Funktionalitäten zur Verfügung gestellt um Webanwendungen online zu stellen. Der
Programmierer kann e.g Routen
oder Schnittstellen zur Interaktion mit einer Datenbank
verwenden und konfigurieren ohne diese von Grund auf selbst implementieren zu müssen.
Flask
Django
Bottle
Tornado
Pyramid
3.8.2.1.2. Flask Intro¶
Im Gegensatz zu den meisten anderen Frameworks verfolgt Flask eine “Do it yourself”
Philosophie d.h. das lediglich essentielle Funktionalität mitgeliefert ist und der
Rest dem Programmierer überlassen wird oder aber in anderen Erweiterungen
(flask-cors
, flask-sqlalchemy
, …)
Import
from flask import Flask
Routing mit “Decorator” über einer Python Methode
@app.route('/something') # definition der Route - /something nach Base-URL ruft Methode auf
def something(): # Python Methoden Definition
return 'something!' # Python Code hier - typisches Output Format = JSON
3.8.2.1.3. AJAX¶
AJAX steht für Asynchronous JavaScript And XML
und erlaubt es Webseiten “asynchron”
Daten mit einem Web-server auszutauschen.\
asynchron weil der Datenverkehr das Skript nicht blockiert und im Hintergrund abläuft bei “success” also wenn die Antwort korrekt am Client angekommen ist wird dann dafür vorgesehener Code ausgeführt
Das Gegenteil wäre ein synchroner Datenaustausch der aber dann den Client zwingt auf eine Antwort zu warten - im Fall einer Webseite bedeutet das stillstand weil der Request solange blockiert bis die gesamte Antwort eingetroffen ist
3.8.2.1.4. GET und POST¶
$.get(): kodiert die Daten/Parameter direkt in der URL und ist damit begrenzt
Beispiel (offen ersichtlich z.b in der Adresszeile im Browser):
?vorname=Foo&nachname=Bar
$.post(): lädt die Daten im sogenannten “Body” der Anfrage und kann beliebig Größe haben\
GET ist also für die Übertragung weniger Information durchaus geeignet, aber stark beschränkt
hinsichtlich der Größe der zu übertragenden Daten. Diese Art des Datenaustausch unterliegt
diversen Beschränkungen von Webseiten, Browser, Server etc. und sollte eine Größe von etwa
2 Kilobyte nicht überschreiten.
(Neben Daten wird jedes Trennzeichen jeder Name von Variablen und Feldern und Bezeichner
mit übertragen und zählt zu dem Limit)
POST sollte verwendet werden wenn die Datengröße nicht nach oben beschränkt ist zum Beispiel
beim Übertragen eines Eingabetextes aus einer “TEXTAREA(HTML)”, dem Upload einer Datei oder
wenn es sich um kritische Daten handelt (e.g. Formular Werte die nicht in der URL angezeigt
werden sollen).
3.8.2.1.5. Vor- und Nachteile GET&POST¶
GET | POST |
---|---|
PRO | PRO |
Einfache Übergabe von Variablen in Links |
unbegrenzte Übertragungsgröße |
Webseite inkl. Variablen als Favorit speichern |
Übertragung von Dateien |
CON | CON |
begrenzte Größe & beschränkt auf ASCII codes |
POST Werte nicht in URLs/Links speicherbar |
keine Dateienübertragung |
3.8.2.1.6. GET Beispiel¶
GET Request - alert
Ergebnis
$.get("http://someHOST:somePORT", function(data){
alert("Data: " + data);
});
3.8.2.1.7. POST Beispiel¶
POST Request
// snippet from W3: https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_ajax_post
// go to this website for interactive and more detailed example
$.post("demo_test_post.asp",
{
name: "Donald Duck",
city: "Duckburg"
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
3.8.2.1.8. Beispiel mit “success” und “error”¶
$.ajax({
url: "http://someHOST:somePORT",
type: "POST",
crossDomain: true,
data: yourData,
dataType: "json",
success: function (response) {
//code to execute when all went well
},
error: function (xhr, status) {
//handle error here
}
});
3.8.2.2. Aufgabe 1¶
Herunterladen des HTML-Templates für die Übungswebseite: https://www.cis.uni-muenchen.de/kurse/max/wast/data/wittfind-wf-template.zip
3.8.2.3. Aufgabe 2¶
Schreiben Sie einen Server mit Flask der lokal auf Ihrem Rechner läuft. ( HOST:localhost, PORT:default - http://localhost:5000)
3.8.2.3.1. Aufgabe 2.1¶
Der Server soll eine Route “@app.route(‘/hello’)” enthalten welche bei Aufruf “Hello World” zurückgibt Optional Der Server definiert einen “@app.errorhandler(404)”
-> {“response” : “Hello, world!”}
3.8.2.3.2. Lösungsvorschlag¶
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, jsonify, abort
app = Flask(__name__)
@app.route('/hello')
def hello_world():
return 'Hello, world!' # Print 'Hello, world!' as the response body
@app.errorhandler(404)
def page_not_found():
return 'This page does not exist! Try localhost:5000/hello', 404
if __name__ == "__main__":
app.run()
3.8.2.4. Aufgabe 3¶
Schreiben Sie eine Javascript Funktion “sendRequest1()” in der Sie den für diese Aufgabe vorbereiteten Button (id=”aufgabe3-button”) einen Ajax-Request ausführen lassen.
3.8.2.4.1. Aufgabe 3.1¶
Die Funktion ist in der HTML-Datei vordefiniert (Zeile 32) und wird mit “onclick” von dem Button aufgerufen. Die Methode “mult” bildet das Produkt der beiden über Ajax gesendeten Argumente.
3.8.2.4.2. Flask Methode als Erweiterung¶
@app.route('/mult/<arg1>/<arg2>')
def mult(arg1, arg2):
result = "NaN"
try:
result = int(arg1)*int(arg2)
except:
return 'Arguments incompatible with method. Result =' + result
finally:
return arg1 + ' * ' + arg2 + ' = ' + str(result)
3.8.2.4.3. Lösungsvorschlag¶
var num1 = $("#num1").val();
var num2 = $("#num2").val();
var host = "localhost"
var port = 5000
$.get("http://" + host + ":" + port + "/mult/" + num1 + "/" + num2, function (response) {
alert(response);
});
3.8.2.5. Aufgabe 4¶
3.8.2.5.1. Aufgabe 4.1¶
Erstellen Sie eine weitere Route in der Flask-anwendung in der ein JSON-Objekt
von der Webseite empfangen wird und die KEY-VALUE Paare vertauscht zurückgegeben
werden.
Wichtig CORS soll unterstützt sein.app = Flask(__name__)
CORS(app)
3.8.2.5.2. Aufgabe 4.2¶
Erweitern sie außerdem die Methode “sendRequest2()” in der “dhd.html” Datei um einen POST Request der das Objekt an den Server sendet. (Erstellen Sie ein Objekt wie unten beschrieben)
3.8.2.5.3. Beispiel¶
Input = {“key” : “test”, “key2” : “test2”, “key3” : “test3”}
Output = {“test” : “key”, “test2” : “key2”, “test3” : “key3”}
3.8.2.5.4. Lösung¶
Flask
var num1 = $("#num1").val();
var num2 = $("#num2").val();
var host = "localhost"
var port = 5000
$.get("http://" + host + ":" + port + "/mult/" + num1 + "/" + num2, function (response) {
alert(response);
});
Javascript AJAX POST
@app.route('/keys', methods=['POST'])
def keys():
response = {}
try:
json_data = request.get_json(force=True)
for key, value in json_data.items():
response[value] = key
except Exception as e:
abort(400, 'Unexpected Error occurred')
return jsonify(response)
3.8.3. Flask¶
3.8.3.1. Was ist Flask?¶
Flask ist ein Micro-Webframework für Python und erspart dem Entwickler grundlegende Mechanismen selbst zu implementieren. Flask wird für die Programmierung von Wepapplikationen genutzt und besteht im Gegensatz zu anderen bekannenten Frameworks wie Django nur aus den elementaren Funktionen und Komponenten um eine Webanwendung online verfügbar zu machen.
3.8.3.2. Flask Installation¶
https://flask.palletsprojects.com/en/1.1.x/installation/
Wir arbeiten mit der empfohlenen Python Version - Python 3.5 oder neuer. Die
Installation erfolgt über “pip”.
pip install flask
pip install flask_cors
3.8.3.2.1. Kurzbeschreibung CORS¶
https://developer.mozilla.org/de/docs/Web/HTTP/CORS
Cross-Origin Resource Sharing (CORS) ist ein Mechanismus, der zusätzliche HTTP
Header verwendet um einem Browser mitzuteilen, dass er einer Webanwendung, die
auf einer anderen Domain(Origin) läuft, die Berechtigung erteilt auf ausgewählte
Ressourcen von einem Server eines anderen Ursprungs(Origin) zuzugreifen.
Eine Webanwendung stellt eine cross-origin HTTP-Anfage, wenn sie eine Ressource
anfordert, die einen anderen Ursprung(Domain, Protokoll und Port) hat, als
ihren eigenen.
Aus Sicherheitsgründen beschränken Browser die aus Skripten heraus initiierten
HTTP-Anfragen mit unterschiedlichen Herkunftsbezeichnungen. Beispielsweise folgen
XMLHttpRequest und die Fetch-API der Richtlinie gleichen Ursprungs.
Das bedeutet, dass eine Webanwendung, die diese APIs verwendet, nur
HTTP-Ressourcen aus der gleichen Herkunft anfordern kann, aus der die Anwendung
geladen wurde, es sei denn, die Antwort aus der anderen Herkunft enthält
die richtigen CORS-Header.\
3.8.3.2.2. Warum Flask-CORS ?¶
Es handelt sich um eine Erweiterung für “Cross Origin Resource Sharing (CORS)” diese ermöglicht “cross-origin” AJAX requests.\
Flask mit default Einstellungen für CORS - unterstützt CORS in allen Routen
app = Flask(__name__)
cors = CORS(app)
@app.route("/")
def helloWorld():
return "Hello, cross-origin-world!"
Erweiterung als einfacher “decorator” (@cross_origin()
) nach der Definition einer Route.
Diese Verwendung führt dazu, dass genau diese Route CORS unterstützt.
@app.route("/")
@cross_origin() # decorator
def helloWorld():
return "Hello, cross-origin-world!"
3.8.3.3. Flask Code Beispiel¶
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask, jsonify, abort
import json
app = Flask(__name__)
def load_json():
# just a dict here actually
data = {
"1-2": {"1:": "You did it!", "2:": {"3:": "You did it!", "4:": {"1:": "You did it!"}}},
"name": "John",
"age": 30,
"city": "New York"
}
return data
@app.route('/')
def hello_world():
return 'Hello, world!' # Print 'Hello, world!' as the response body
@app.route('/<arg1>/<arg2>')
def do_something(arg1, arg2):
return 'Did something with ' + arg1 + ' & ' + arg2 + '! (Try localhost:5000/mult/<arg1>/<arg2>)'
@app.route('/mult/<arg1>/<arg2>')
def mult(arg1, arg2):
result = "NaN"
try:
result = int(arg1)*int(arg2)
except:
pass
finally:
return arg1 + ' * ' + arg2 + ' = ' + str(result)
@app.route('/json/<arg1>/<arg2>')
def json(arg1, arg2):
data = load_json()
key = str(arg1) + "-" + str(arg2)
if key in data:
return jsonify(data[key])
else:
return abort(404)
@app.errorhandler(404)
def page_not_found():
return 'This page does not exist! Try localhost:5000/json/1/2', 404
if __name__ == "__main__":
app.run()