schule:rest_in_15_minuten
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| schule:rest_in_15_minuten [2015-12-07 19:23] – +Server mit nodejs marco.bakera | schule:rest_in_15_minuten [2017-04-19 08:27] (aktuell) – [REST in 15 Minuten] added hug marco.bakera | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| + | ====== REST in 15 Minuten ====== | ||
| + | |||
| + | Bei [[wpde> | ||
| + | Anwendungen handelt es sich um einfache Versionen von Webservices. REST | ||
| + | steht für REpresentational State Transfer. Dahinter verbergen sich verschiedene Regeln, | ||
| + | die sich stark an etablierte Konzepte von HTTP | ||
| + | anlehnen. Da es eine sehr lose und einfache Spezifikation ist, kann sie jeder | ||
| + | einfach umsetzen und, weil sich die Spezifikation stark an HTTP | ||
| + | anlehnt, ist die Umsetzung mit vielen unterschiedlichen Programmiersprachen | ||
| + | möglich. Ich habe mich entschieden, | ||
| + | testen. Dafür habe ich das Webframework [[http:// | ||
| + | verwendet. Eine weitere und einfachere Möglichkeit bietet das Paket [[http:// | ||
| + | |||
| + | Ein zentrales Element in REST und auch HTTP sind die Ressourcen, die über eine | ||
| + | URI bzw. URL erreichbar sind. Das können | ||
| + | Datenbankobjekte oder Dienste sein. Bei der Umsetzung von REST mit HTTP werden | ||
| + | die unterschiedlichen Methoden von HTTP genutzt: GET zum Abrufen | ||
| + | und POST zum Speichern und Erstellen von Daten. Wie das Ergebnis | ||
| + | aussehen soll, kann zusätzlich in den Headerinformationen des HTTP requests eingetragen | ||
| + | werden. | ||
| + | |||
| + | ===== Quelltext des Webservices ===== | ||
| + | |||
| + | Als Beispiel habe ich eine kleine Zitatesammlung gewählt, die aus einer Liste von Zitaten besteht. | ||
| + | Es können neue Zitate hinzugefügt und die bestehenden Zitate angezeigt werden. | ||
| + | Schauen wir uns den Quelltext etwas genauer an. Ich habe ihn in der Datei rest.py abgelegt. | ||
| + | |||
| + | <file python rest.py> | ||
| + | # coding=UTF8 | ||
| + | # ^^^^^^^^^^^ | ||
| + | # Mit der ersten Zeile legen wir die Kodierung der Quelltextdatei fest. Dies | ||
| + | # ist nötig, damit wir hier auch deutsche Umlaute benutzen können. | ||
| + | | ||
| + | # Die Anwendung wurde mit dem Bottle-Framework (http:// | ||
| + | # erstellt. Dies ist ein einfaches Webframework für Python Anwendungen. Wir | ||
| + | # importieren zunächst die notwendigen Methoden. | ||
| + | from bottle import route, run, debug, request, response | ||
| + | | ||
| + | # In dem Dictionary zitate sollen Zitate gesammelt werden. Jedes Zitat ist | ||
| + | # über eine ID erreichbar: {0: "Zitat 1", 1 : "Zitat 2", ...} | ||
| + | zitate = {} | ||
| + | | ||
| + | # Mit route wird eine Ressource festgelegt. Dies ist der Teil, der hinter der | ||
| + | # Adresse des Hostnamens folgt. Eine Anfrage wird normalerweise über ein | ||
| + | # GET-request entgegen genommen. In diesem Fall wird eine Liste aller Zitate | ||
| + | # ausgegeben. Es ist guter Stil, den Namen des Ressource jeweils im Plural zu | ||
| + | # verwenden. | ||
| + | @route('/ | ||
| + | def zitate_liste(): | ||
| + | # Über den Header können wir prüfen, welche Form der Ausgabe der Client | ||
| + | # erwartet. Da wir bei REST nur von Repräsentationen ausgehen, können die | ||
| + | # Inhalte unterschiedlich dargestellt und damit auch angefordert | ||
| + | # werden. Wir unterstützen HTML und Json als Formate. | ||
| + | if request.get_header(' | ||
| + | return zitate | ||
| + | else: | ||
| + | # Wenn kein besonderes Format angefordert wurde, wird HTML | ||
| + | # ausgegeben. Wir stecken die Zitate in eine Tabelle und geben diese | ||
| + | # zurück. | ||
| + | html = "< | ||
| + | html += "< | ||
| + | for k in zitate: | ||
| + | html += "< | ||
| + | html += "< | ||
| + | html += "</ | ||
| + | html += "</ | ||
| + | return html | ||
| + | | ||
| + | # Ein bestimmtes Zitat kann über seine Nummer angesprochen und einzeln | ||
| + | # ausgegeben werden. Der Parameter <nr> in der URL wird an die Methode | ||
| + | # übergeben. | ||
| + | @route('/ | ||
| + | def zitate_get(nr): | ||
| + | zid = int(nr) | ||
| + | if zid in zitate: | ||
| + | return zitate[zid] | ||
| + | else: | ||
| + | # Die Nummer und damit das Zitat wurden nicht gefunden. | ||
| + | # Über den Statuscode melden wir den Misserfolg an den Client. | ||
| + | # Status 404: Not Found | ||
| + | response.status = 404 | ||
| + | return " | ||
| + | | ||
| + | # Damit wir etwas für die Anzeige haben, müssen auch neue Zitate eingetragen | ||
| + | # werden können. Diese geschieht über die HTTP Post-Methode. Die Daten, also | ||
| + | # der Text des Zitates, wird über den Body des requests übertragen. In der | ||
| + | # Antwort wird der Client über die Adresse der neuen Ressource | ||
| + | # informiert. Zusätzlich setzen wir die neue Adresse im Headerfeld Location. | ||
| + | # Schließlich wird der Status der Antwort auf 201 (Created) gesetzt. Damit die | ||
| + | # Methode nur auf POST reagiert, wird die route entsprechend konfiguriert. | ||
| + | @route('/ | ||
| + | def zitat_erstellen(): | ||
| + | # Die ID für das nächste Zitat wird bestimmt. Wenn es noch keine Zitate | ||
| + | # gibt, ist es die 0, ansonsten wird die größte ID um 1 erhöht. | ||
| + | if len(zitate) == 0: | ||
| + | neue_id = 0 | ||
| + | else: | ||
| + | neue_id = max(zitate.keys()) + 1 | ||
| + | |||
| + | zitate[neue_id] = request.body.readline() | ||
| + | response.set_header(" | ||
| + | response.status = 201 # 201: Created | ||
| + | |||
| + | # Als Ergebnis geben wir die komplette URI des neuen Zitates zurück | ||
| + | return request.url + "/" | ||
| + | | ||
| + | # Anmerkung: Komplexe Einträge wie z.B. ein Zitat mit Informationen über den | ||
| + | # Autor und das Jahr der Verwendung werden nicht als Text, sondern als Json- | ||
| + | # oder XML-Inhalt übermittelt. Es sind aber auch binäre Daten wie Bilder oder | ||
| + | # MP3-Dateien möglich. | ||
| + | | ||
| + | # | ||
| + | # START des Webservers auf Port 8000. | ||
| + | # | ||
| + | | ||
| + | debug(True) | ||
| + | run(port=8000, | ||
| + | </ | ||
| + | |||
| + | ===== Aufruf des Webservices ===== | ||
| + | |||
| + | Nun können wir testweise auf den Web-Service zugreifen. Ich benutze hierfür das | ||
| + | Kommandozeilentool [[wpde> | ||
| + | requests und dem Auslesen der Antwort bietet. Zunächst rufen wir die Adresse | ||
| + | einfach nur auf. Mit der Option -i werden die Headerinformationen zusätzlich | ||
| + | angezeigt. | ||
| + | |||
| + | <code bash> | ||
| + | $ curl localhost: | ||
| + | < | ||
| + | </ | ||
| + | |||
| + | Ohne weitere Angaben wird HTML als Ausgabeformat produziert. Dies könnte ein | ||
| + | Browser sogar anzeigen. Da wir noch keine Zitate hinzugefügt haben, zeigt die | ||
| + | Ausgabe eine leere Tabelle. | ||
| + | |||
| + | Probieren wir es nun mit einer anderen Variante und fordern Json als | ||
| + | Ausgabeformat an. Die URI ist die gleiche, denn es ist ja die gleiche | ||
| + | Ressource, auf die ich zugreifen möchte. Lediglich die Darstellung soll eine | ||
| + | andere sein. Daher müssen wir ein Headerfeld entsprechend konfigurieren. Mit | ||
| + | der Option -H können wir dies tun. | ||
| + | |||
| + | <code bash> | ||
| + | $ curl -H " | ||
| + | {} | ||
| + | </ | ||
| + | |||
| + | Nun wird Json als Rückgabeformat verwendet. Im Moment ist unser Ergebnis | ||
| + | noch eine leere Zitatesammlung. | ||
| + | |||
| + | Wir können aber nicht nur Daten ausgeben lassen, sondern auch setzen oder | ||
| + | erstellen. Dazu muss eine HTTP POST Methode aufgerufen werden. Wir nehmen | ||
| + | diesmal die gleiche Ressource - also URI - und generieren eine POST Anfrage | ||
| + | mit -d. Dieser Parameter erwartet noch Daten, die wird senden wollen. Wir | ||
| + | geben hier nur den Text eines Zitates an. | ||
| + | |||
| + | <code bash> | ||
| + | $ curl -d "The cake is a lie." localhost: | ||
| + | http:// | ||
| + | </ | ||
| + | |||
| + | Als Ergebnis erhalten wir eine URL mit dem neu erstellten Zitat. Fügen wir | ||
| + | noch ein paar Zitate hinzu. | ||
| + | |||
| + | <code bash> | ||
| + | $ curl -d "Go To Statement Considered Harmful." | ||
| + | http:// | ||
| + | |||
| + | $ curl -d "foo bar" localhost: | ||
| + | http:// | ||
| + | </ | ||
| + | |||
| + | Das Ergebnis können wir uns erneut ausgeben lassen. | ||
| + | |||
| + | <code bash> | ||
| + | $ curl -H " | ||
| + | |||
| + | {" | ||
| + | "foo bar"} | ||
| + | </ | ||
| + | |||
| + | ===== Wie geht's weiter? ===== | ||
| + | |||
| + | Das obige Beispiel ist ein erster Einstieg in REST. Das Programm kann um verschiedene Aspekte erweitert werden. Greif dir doch einen Aspekt heraus und probiere es einmal selbst. | ||
| + | |||
| + | * Ein Zitat kann mittels der HTTP-Methode DELETE gelöscht werden. | ||
| + | * Informationen über Zitate werden angereichert um z.B. den Autor. | ||
| + | * Für verändernde Operationen wie das Anlegen eines neuen Zitates muss man sich mit Nutzerdaten anmelden. | ||
| + | |||
| + | ===== Server mit nodejs ===== | ||
| + | |||
| + | Mit [[wpde> | ||
| + | |||
| + | <code javascript> | ||
| + | var http = require(' | ||
| + | |||
| + | // Erstelle den Server. Der Parameter ist eine | ||
| + | // Callback-Methode, | ||
| + | var server = http.createServer( | ||
| + | function (req, res) { | ||
| + | res.write(' | ||
| + | res.write(" | ||
| + | res.write(" | ||
| + | res.write(' | ||
| + | res.end(); | ||
| + | }); | ||
| + | |||
| + | |||
| + | server.listen(12345); | ||
| + | </ | ||
| + | |||
| + | Bei einem Aufruf der URL ''< | ||
| + | |||
| + | Request erhalten. | ||
| + | Method: GET | ||
| + | URL: /hallo | ||
| + | Hallo Client | ||
| + | ===== Weitere Links ===== | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | * [[https:// | ||
| + | * [[wpde> | ||
| + | * [[http:// | ||
| + | * [[Webserver programmieren]] | ||
| + | * [[https:// | ||
| + | * [[http:// | ||
| + | |||
