• Save
Web Services leicht gemacht
Upcoming SlideShare
Loading in...5
×
 

Web Services leicht gemacht

on

  • 1,798 views

Web Services leicht gemacht - REST-basierte Schnittstellen mit dem Symfony-Framework implementieren". Artikel von mir, den ich in der t3n Nr. 17 veröffentlicht habe.

Web Services leicht gemacht - REST-basierte Schnittstellen mit dem Symfony-Framework implementieren". Artikel von mir, den ich in der t3n Nr. 17 veröffentlicht habe.

Statistics

Views

Total Views
1,798
Views on SlideShare
1,797
Embed Views
1

Actions

Likes
1
Downloads
0
Comments
0

1 Embed 1

http://www.sfexception.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Web Services leicht gemacht Web Services leicht gemacht Document Transcript

  • Web Services leicht gemachtREST-basierte Schnittstellen mit dem Symfony-Framework implementierenIm Web 2.0 verfügen praktisch alle großen Anbieter über eine API. Application Programming Interfacesermöglichen anderen Applikationen den kontrollierten Zugang zu den eigenen Daten und bilden so dieGrundlage für Mash-Ups oder Third-Party-Applikationen. Das PHP-basierte MVC-Framework Symfonymacht es einfach, derartige Schnittstellen effizient und sauber zu entwickeln. Dieser Artikel beleuchtetden Aufbau von solchen Application Programming Interfaces und demonstriert, wie sie sich mit Hilfe desSymfony-Frameworks implementieren lassen. ↘ Daniel HallerREST-basierte APIs spielen in den letzten Jahren eine immer wich-tigere Rolle im Web 2.0. Mit Hilfe einer derartigen Schnittstellekönnen Anbieter von Webdiensten anderen Anwendungen Datenin kontrolliertem Umfang bereit stellen und sich auf diese WeiseZielgruppen jenseits der eigenen Website erschließen. APIs sind sozu einem sehr wichtigen Erfolgsfaktor geworden – man denke nuran Twitter oder Facebook und deren Ökosystem von Third-Party-Applikationen. Mit Hilfe des Symfony-Frameworks [1] lassen sich derartige APIsauf Basis von REST [2] sehr effektiv umsetzen. Dies lässt sich ambesten anhand eines beispielhaften Anwendungsfalls verdeutli-chen: Ein fiktiver Buchhändler stellt eine Schnittstelle bereit, überden sich Informationen zum Angebot abfragen lassen und mit derVerleger mit Hilfe eines dedizierten Clients neue Bücher direkt inder Datenbank des Händlers speichern können. Symfony ist modular aufgebaut, einzelne Komponenten lassen sich auch ei- genständig verwenden.Die Grundlagen von REST Die API in SymfonyEine REST-basierte Schnittstelle besteht aus Ressourcen, die durch Die Bestandteile einer REST-API lassen sich gut in Symfony abbil-URIs adressierbar sind und in verschiedenen Repräsentationen dar- den: Module beziehungsweise deren Actioncontroller stellen diegestellt werden können. Ressourcen sind Informationen, die eigen- Ressourcen dar, die sich mit Hilfe des Symfony-Routings beliebigständig referenziert werden sollen, beispielsweise Texte, ein adressieren lassen. Dank des MVC-Patterns kann für jede ge-Benutzerprofil oder eine Suchergebnisseite. Deren Repräsentation wünschte Repräsentation ein eigenes Template definiert werden.erfolgt meistens im XML-Format, da dieses Format die gewünschte Das Auslesen des Bücherbestands ist der wohl einfachste An-Interoperabilität zwischen verschiedenen Systemen oder Plattfor- wendungsfall: Der Request an einen URI liefert eine Liste mit Bü-men noch am besten sicherstellen kann. Andere Formate wie JSON chern. Im Grunde unterscheidet sich dieser Anwendungsfall vomsind aber ebenfalls denkbar. Ressourcen lassen sich, wie auch im herkömmlichen Abruf im Browser nur durch die RepräsentationenWeb, miteinander verlinken. der Response, die im XML- anstatt im HTML-Format dargestellt wird. Der Zugriff auf die API erfolgt ganz einfach durch das HTTP- Dem Routing-System von Symfony kommt bei der UmsetzungProtokoll. Die verschiedenen Methoden des Protokolls codieren die einer REST-API eine wichtige Rolle zu. Durch die Routing-RegelnOperationen auf der Anwendungsebene. Wird beispielsweise per wird in erster Linie festgelegt, welchen URI eine Ressource be-GET auf eine Ressource zugegriffen, weiß der Server, dass ein Lese- kommt, mit welcher HTTP-Methode darauf zugegriffen werden darfvorgang durchgeführt werden soll – derselbe Request mit der und welcher Actioncontroller den Request verarbeitet.DELETE-Methode würde die Ressource hingegen löschen (mehrdazu unter [3] ). Eine einfache Routing-Regel booklist: # Darstellung einer Liste mit dem Buchbestand url: /booklist.:sf_format Tools zum Testen von REST-APIs class: sfRequestRoute REST-basierte Schnittstellen lassen sich nicht direkt im Browser ent- param: { module: book, action: list, , sf_format: html } wickeln oder testen, da sich die HTTP-Methoden für den Request nicht requirements: { sf_method: GET, sf_format: (?:xml|json|html) } festlegen lassen und auch kein direkter Zugriff auf weitere Parameter Listing 1 des HTTP-Requests besteht. Abhilfe schafft das Programm „RESTClient“ (http://bit.ly/RESTClient) oder die Firefox-Erweiterung „RestTest“ (http://bit.ly/RESTTest). Im Zusammenhang mit REST-APIs spielt insbesondere der sf_for- mat-Parameter eine wichtige Rolle. Durch ihn wird das Format1 © yeebase media 2009. Veröffentlichung und Vervielfältigung nur mit Genehmigung der yeebase media GbR. http://t3n.yeebase.com
  • bestimmt, in dem die Ressource an den Client ausgeliefert wird. Der bere Trennnung zu den Front- und Backend-Applikationen zu ha-Standardwert ist HTML, erlaubt sind in diesem Beispiel aber auch ben, die in fast jedem Symfony-Projekt vorhanden sind.XML oder JSON. Alternativ lässt sich das Request-Format auch direkt Eine neue API-Applikation samt Book-Modul ist dank der Kom-in der Action festlegen (Listing 2), allerdings ist es eleganter, das mandozeilentools schnell angelegt:direkt über den URI zu tun. API-Applikation genieren Request-Format im Controller setzen $ symfony generate:app api $ symfony generate:module api book $request->setRequestFormat(xml); Listing 3Listing 2 Für das Book-Modul das Scaffolding zu verwenden, ist durchausDurch den sf_method-Parameter wird festgelegt, mit welchen Me- möglich, aber nicht unbedingt sinnvoll: Es werden dann zwar allethoden eine Ressource aufgerufen werden kann. Dies kann beson- nötigen Methoden im Actioncontroller angelegt, diese gehen aberders bei komplexeren APIs praktisch sein – zum Beispiel lässt sich davon aus, dass die Daten zum Anlegen oder Ändern von Daten-so der Request für eine Ressource per DELETE-Methode unterbin- sätzen von den ebenfalls generierten (HTML-)Formularen in denden. Templates stammen. Für die API allerdings müssen diese stattdes- Für komplexere Requests mit mehr Parametern ist es darüber sen aus den HTTP-Bodys extrahiert werden, wo sie im XML-Formathinaus sogar möglich, bereits im Routing eine Validierung der GET- an die Schnittstelle übertragen werden.Parameter per regulärem Ausdruck vorzunehmen. Bei einer dedizierten Applikation für die API kann auch die View- Konfiguration geändert werden (Listing 4): Ein Layout wird jetzt nicht mehr benötigt, da die Templates aus reinem XML bestehen. Außerdem wird auch der standardmäßige Content-Type ange- passt. Anpassungen an der view.yml für die API default: http_metas: content-type: text/xml has_layout: off Listing 4 Symfony und das Doctrine-ORM stellen für das Routing einen nütz- lichen Shortcut bereit:Der Aufbau von Symfony gliedert sich in eine Plattform und das darauf aufset-zende Framework. Shortcut in der routing.yml book:Die Bücherliste kann außer im HTML-Format auch als XML- oder class: sfDoctrineRouteCollection options: { model: Book }JSON-Repräsentation angefordert werden. Dazu müssen für jedesFormat eigene Templates angelegt werden und Symfony wählt auf Listing 5Basis des gesetzten sf_request-Parameters das Passende aus. Aus-schlaggebend ist hierbei die Dateiendung: listSuccess.php für eine Diese Regel wird zu einer Reihe REST-konformer Routen aufgelöst,standardmäßige HTML-Repräsentation, listSuccess.xml.php für ähnlich wie jener in Listing 1, die man sich in der ZusammenfassungXML und listSuccess.json.php für JSON. auch durch die Kommandozeile anzeigen lassen kann: Symfony liefert standardmäßig nur die HTML-Repräsentationmit einem Layout aus, das bei JSON oder XML natürlich entfällt. Routen in der APIAußerdem kümmert sich das Framework selbstständig um den kor- $ symfony app:routesrekten Content-Type (text/xml bzw. text/x-json) im HTTP-Header. >> app Current routes for application Name Method Pattern book GET /book.:sf_formatKomplexer: Datenmanipulation book_new GET book_create POST /book/new.:sf_format /book.:sf_format book_edit GET /book/:id/edit.:sf_forMit den beschriebenen Funktionen hat man im Prinzip bereits alles book_update PUT /book/:id.:sf_formatzusammen, was man braucht, um eine sehr einfache API zum Aus- book_delete DELETE /book/:id.:sf_formatlesen und Bereitstellen von Daten zu implementieren. Anspruchs- book_show GET /book/:id.:sf_formatvoller wird es jedoch, wenn die API nicht nur Daten auslesen, Listing 6sondern auch ändern, löschen oder erstellen soll. Ein weitaus kom-plexeres Anwendungsszenario ist daher die Implementierung einer Diese Routen verweisen auf die jeweiligen Methoden (new, create,Schnittstellenfunktion, mit der ein Verleger neue Bücher direkt aus edit, update usw.) im Actioncontroller des Book-Moduls, die späterdem eigenen Warenwirtschaftssystem in das Shop-System über- implementiert werden müssen.tragen kann. Aus einer „lesenden“ wird so eine „schreibende“ API. Für den Einsatz innerhalb einer API sollten die „Edit“- und Je umfangreicher eine API wird, desto sinnvoller kann es sein, „New“-Methoden nicht implementiert werden beziehungsweisediese in eine eigenständige Applikation auszulagern um eine sau- eine Fehlermeldung werfen, da eine GET-Methode niemals in der© yeebase media 2009. Veröffentlichung und Vervielfältigung nur mit Genehmigung der yeebase media GbR. http://t3n.yeebase.com 2
  • Lage sein sollte, Daten auf einem Server in irgendeiner Weise zu koppelt dabei ein Datenbankobjekt direkt mit dem ID-Parameter inändern (oder zu erstellen). Das ist ausschließlich den idempotenten der URL, sodass man im Controller darauf zugreifen und sich eigeneMethoden PUT, POST und DELETE vorbehalten. Queries weitgehend sparen kann. Wird kein Datensatz mit der ge- wünschten ID gefunden, wirft Symfony einen 404-Fehler. Die Im-Authentifizierung durch API-Keys plementierung der Controller-Methoden geht daher schnell von der Hand. Um die Dateilansicht eines Buchs anzuzeigen, reicht eineNatürlich sollen nur autorisierte Benutzer Datensätze erstellen oder Zeile im Controller:ändern dürfen. Bevor also eine Aktion ausgeführt wird, muss sichder Benutzer zunächst gegenüber der API authentifizieren. Eine Detailansicht eines BuchsMöglichkeit dazu ist die Verwendung von API-Keys. Der API-Keykann im HTTP-Header (zum Beispiel als „X-ApiKey“) mitgeschickt // HTTP-Request: GET /book/[id] HTTP/1.1 public function executeShow(sfWebRequest $request) {werden und dient als Ersatz für die übliche Benutzernamen/Pass- $this->book = $this->getRoute()->getObject(); // $this->book is now availablewort-Kombination (sinnvollerweise sollten Requests an die API in in the templatediesem Fall per HTTP/S gesichert werden). Die Authentifizierung }kann in Symfony als Filter (Listing 7) implementiert werden, der vor Listing 9dem Abarbeiten des eigentlichen Requests im Controller durch-laufen wird und in der filter.yml aktiviert werden muss. Ein Request für denselben URI mit der DELETE-Methode wird REST- konform zur Delete-Methode geroutet: API-Filter Klasse class apiFilter extends sfFilter { Ein Buch löschen public function execute($filterChain) { if ($this->isFirstCall()) { // HTTP-Request: DELETE /book/[id] HTTP/1.1 $request = $this->getContext()->getRequest(); public function executeDelete(sfWebRequest $request) { $session = $this->getContext()->getUser(); $book = $this->getRoute()->getObject(); // Get API-Key from HTTP-Header // User allowed to delete this Book? If not, stop execution and send HTTP- $apikey = $request->getHttpHeader(X-ApiKey); Statuscode 403 // Select user from database by given API-Key $this->checkPermission($book); $userprofile = Doctrine::getTable(User)->findOneByApikey($apikey); // ...User has Permission, lets delete this Book // Throw an error if user cannot be found... $book->delete(); jeHTTPError::forwardUnless($userprofile, 401, Invalid or missing API-Key); } // ...or save the user in the session $session->setAttribute(user,$userprofile); Listing 10 } // Execute next filter in filter symfonys filter chain $filterChain->execute(); Mit den POST- und PUT-Methoden wird ein neues Buch angelegt // Code to execute AFTER the action execution, before the rendering beziehungsweise ein bestehendes Buch geändert. Die dazu erfor- // Delete User-Session after every API-Request derlichen Daten werden als XML im HTTP-Body übertragen und session_unset(); session_destroy(); müssen daher ausgelesen und geparst werden. Hierzu kann man } im lib-Verzeichnis in einer Klasse eine einfache Methode (Listing 11) } implementieren, die bei Gebrauch durch den Autoload-Mechanis-Listing 7 mus von Symfony inkludiert wird.Dieser Filter lädt zum einen das Benutzerobjekt anhand des API- Auslesen der XML-NutzlastKeys im Header aus der Datenbank und speichert es in der Symfony- class Tools {Benutzersession, sodass es überall im Code zur Verfügung steht. Ist public static function getRequestBody () {der API-Key nicht korrekt, wird stattdessen eine Fehlermeldung mit $stream = fopen("php://input", "r"); $data = stream_get_contents($stream);entsprechendem HTTP-Statuscode gesendet. Zum anderen muss fclose($stream);die Session wieder gelöscht werden, nachdem der Request abge- return simplexml_load_string($data);arbeitet wurde. REST ist per Definition zustandslos, das heisst eine } }„klassische“ Session wird nicht gespeichert. Für das Error-Handling kann wie in Listing 7 das jeHTTPError- Listing 11Exception-Plugin genutzt werden, welches das Symfony-ErrorHandling um die Fähigkeit erweitert, beliebige HTTP-Statuscodes Ein neues Buch lässt sich ebenfalls sehr einfach erzeugen:und Fehlermeldungen in der Response zurückzugeben. Gerade imZusammenhang mit REST ist das eine praktische Sache. Das Plugin Ein neues Buch anlegenlässt sich mit Hilfe des CLI schnell installieren: // HTTP-Request: POST /book HTTP/1.1 public function executeCreate(sfWebRequest $request) { Plugin installieren $data = Tools::getRequestBody(); $book = new Book(); $book->saveBook($data); // Save Book in the Model $ symfony plugin:install jeHTTPErrorExceptionPlugin $this->getResponse()->setStatusCode(201); $this->getResponse()->setHttpHeader(Location,Listing 8 $request->getHost()./book/.$book->get(id)); }Der Controller des Book-Moduls Listing 12In Listing 5 werden Routen auf die Index-, Show-, Create-, Update-,und Delete-Methoden des Book-Controllers erzeugt, die imple- In dieser Methode werden die Daten zunächst aus dem Request-mentiert werden müssen. Die sfDoctrineRouteCollection-Klasse Body extrahiert und an das Model übergeben, wo das neue Buch3 © yeebase media 2009. Veröffentlichung und Vervielfältigung nur mit Genehmigung der yeebase media GbR. http://t3n.yeebase.com
  • dann angelegt wird. Wichtig ist, dem Client im Response per HTTP-Statuscode mitzuteilen, dass die Ressource erzeugt wurde („201 –Created“) und deren (neue) URI im Location-Header anzugeben. Analog dazu funktioniert das Editieren eines Buchs: Ein bestehendes Buch editieren // HTTP-Request: PUT /book/[id] HTTP/1.1 public function executeUpdate(sfWebRequest $request) { $data = Tools::getRequestBody(); $book = $this->getRoute()->getObject(); // User allowed to edit Book? If not, stop execution, send HTTP-Statuscode 403 $this->checkPermission($book); $book->saveBook($data); }Listing 13Für alle genannten Methoden müssen natürlich auch die jeweiligen(XML-)Templates angelegt werden, um einem Client das ge-wünschte Feedback – zum Beispiel in Form einer Statusmeldung –geben zu können. Für die Validierung der Benutzerdaten bringtSymfony eine Reihe von Standard-Validatoren mit, die allerdingsprimär zur Verwendung mit dem Formular-Framework gedachtsind. Noch einfacher lassen sich alternativ die Validatoren des Doc-trine-ORMs [4] verwenden, die man bereits in der Schema-Dateikonfigurieren kann und die die Daten noch auf der Anwendungs-ebene unmittelbar vor dem Speichern in der Datenbank validieren.ZusammenfassungMit Hilfe des Symfony-Frameworks lassen sich REST-basierteSchnittstellen sehr sauber und vergleichsweise einfach implemen-tieren. Dieser Artikel zeigt nur einen von vielen Ansätzen, der sichaber grundsätzlich auch für umfangreiche APIs eignet – selbst wennfür den produktiven Einsatz noch einige Aspekte tiefer ausgearbei-tet werden müssten. ↔Links und Literatur ↗ Softlink 2426[1] Offizielle Website des Symfony-Projekts: http://www.symfony- project.org/[2] Wikipedia-Eintrag zu REST: http://de.wikipedia.org/wiki/ Representational_State_Transfer[3] OIO: REST Web Services: http://www.oio.de/public/xml/rest- webservices.pdf[4] Doctrine Data Validation: http://www.doctrine-project.org/ documentation/manual/1_1/en/data-validation Der Autor Daniel Haller ist bei der Frankfurter Agen- tur BlueMars als Technischer Projektma- nager und Web Developer tätig. Mit REST- basierten APIs hat er sich im Rahmen seiner Diplomarbeit zum Media System Designer in Darmstadt beschäftigt. Er ist begeister- ter Anwender des Symfony-Frameworks.© yeebase media 2009. Veröffentlichung und Vervielfältigung nur mit Genehmigung der yeebase media GbR. http://t3n.yeebase.com 4