New Media Design & Development III - The Symfony Framework Academiejaar 2014-2015, Arteveldehogeschool ©2014 Olivier Parent
[TOC]
http://www.ibm.com/developerworks/webservices/library/ws-restful/ http://martinfowler.com/articles/richardsonMaturityModel.html http://symfony.com/video/26/designing-http-interfaces-and-restful-web-services/English http://welcometothebundle.com/symfony2-rest-api-the-best-2013-way/
REST (Representational State Transfer) is een architecturale stijl voor software die in 2000 door Roy Thomas Fielding beschreven werd in zijn doctoraatsthesis Architectural Styles and the Design of Network-based Software Architectures aan de University of California, Irvine.
De mate waarin de REST architeturale stijl geïmplementeerd wordt, wordt weergegeven op de schaal van het Richardson Maturity Model (RMM) van Leonard Richardson:
- Niveau 0 — HTTP
- Niveau 1 — Resources: de complexiteit van de web service wordt opgedeeld in resources.
- Niveau 2 — HTTP-methoden: zodat de handelingen op de resources op een uniforme manier kunnen gebeuren.
- Niveau 3 — HATEOAS (Hypermedia as the Engine of Application State): de mogelijkheden van de web service zijn te ontdekken, want de koppelingen (hyperlinks) en relaties (IANA Link Relations) naar mogelijke acties die volgen op de laatste actie zitten in de hypermedia. XML en JSON zijn geen hypermedia, maar er kunnen hypermedia van gemaakt worden, bijv. HTML, XHTML en Atom.
RMM Niveau 3 is RESTful en is vereist om te voldoen aan de REST architecturale stijl, maar is op zich nog altijd niet voldoende om over REST te kunnen spreken. Dus: REST is altijd RESTful, maar RESTful is niet noodzakelijk REST.
Opmerking: Veel API's implementeren RMM Niveau 3 niet, en dat is oké want RESTful kan in veel gevallen overkill zijn. Maar strict gezien is het dan geen RESTful API en al zeker geen REST API!
Bron: http://www.ibm.com/developerworks/webservices/library/ws-restful/
De kenmerken van een RESTful Web Service:
- Maakt expliciet gebruik van HTTP-methoden (
POST
,GET
,PUT
,DELETE
…) om acties uit te voeren op Resources - Gebruikt HTTP-statuscodes (
200
,204
,404
…) om weer te geven of het resultaat succesvol was. - Is stateless (de server moet geen state (de opgeslagen infomatie op een bepaald tijdstip) bijhouden die invloed hebben op de response op een request)
- URI's die een mappenstructuur voorstellen (uniform interface?)
- Transfer view dus: HTML, XML, XHTML, JSON, e.d.
Opmerking:
Een URI (Uniform Resource Identifier) bestaat uit een URL (Uniform Resource Locator) en/of URN (Uniform Resource Name)
Stateful is bijvoorbeeld:
http://www.example.com/users
Welke User-resource de URI voorstelt hangt af van wie aangemeld is.
Stateless is bijvoorbeeld:
http://www.example.com/users/123
De URI stelt altijd de User-resource voor die 123
als identifier heeft. De gegevens van User 123
kunnen dan wel veranderen, maar het blijft altijd User 123
. Hierdoor kan de server de response cachen.
- http://www.example.com/users/ → lijst met gebruikers
- http://www.example.com/users/?filter=admin&sort=desc → gefilterde en gesorteerde lijst gebruikers
- http://www.example.com/users/123
→ enkele gebruiker (met identifier
123
) - http://www.example.com/users/123/photos/
→ alle foto's van gebruiker met identifier
123
- …
Symfony heeft out-of-the-box een vrij beperkte ondersteuning voor RESTful API's, maar gelukkig bestaat er een goede Bundle van FOS.
Weetje:
FOS (FriendsOfSymfony) is een intiatief van enkele Symfony2-developers van KNP Labs. FOS heeft enkele van de 30 meest nuttige Symfony Bundles ontwikkeld.
Bron: http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/
HTTP-methoden worden ook HTTP-werkwoorden (HTTP verbs) genoemd.
HTTP-methode | Betekenis | CRUD-mapping | Idempotent? | Veilig? | Opmerking |
---|---|---|---|---|---|
POST |
Resource aanmaken | CREATE |
Nee | Nee | |
GET |
Resource opvragen | READ |
Ja | Ja | message body verplicht |
HEAD |
Resource opvragen | READ |
Ja | Ja | message body niet verplicht |
PATCH |
Resource wijzigen | UPDATE |
Ja | Nee | |
PUT |
Resource vervangen | UPDATE |
Ja | Nee | |
DELETE |
Resource verwijderen | DELETE |
Ja | Nee |
Opmerkingen:
- Zie ook: Hypertext Transfer Protocol (HTTP/1.1): Request Methods
- Veilig: er kunnen geen ongewenste dingen gebeuren (zoals bijvoorbeeld het wijzigen van gegevens).
- Idempotent: zelfde effect ongeacht hoeveel keer de request herhaald wordt. Daardoor mogelijk om de response te cachen door bijvoorbeeld een reverse proxy proxyserver zoals Varnish Cache.
Een moderne User Agent (bijv. browser of app) heeft een Same-Origin Security Policy. Dit wil zeggen dat de bronnen (resources), zoals bijvoorbeeld JavaScript-bestanden of gegevens, allen dezelfde oorsprong (protocol, domein en poort) moeten hebben, zoniet worden ze geweigerd.
Eén manier om deze beperking te omzeilen is werken met JSON-P, maar daar kan je enkel gegevens mee inlezen. Een andere manier is werken met een proxyscript op hetzelfde domein als de User Agent, maar dit is niet altijd mogelijk
De betere manier is CORS inschakelen. CORS is een W3C Recommendation die door moderne User Agents geïmplementeerd is.
De server moet aangeven dat CORS ingeschakeld is voor het domein dat een request stuurt. Dit kan eenvoudig door een Access-Control-Allow-Origin
in de header van de response te zetten met een van deze opties:
*
voor alle domeinen;- een echo van het domein waarvan de request komt (dit komt op hetzelfde neer als de eerste optie);
- bijv.
http://www.example.org
voor een welbepaald domein.
Je kan dit doen in PHP met:
// Laat alle domeinen toe.
header('Access-Control-Allow-Origin: *');
Of in de configuratie van de server (in dit geval NGINX):
# Laat alle domeinen toe.
add_header Access-Control-Allow-Origin: '*';
Opgelet: in bovenstaand voorbeeld zijn de aanhalingstekens (
'
) van belang! Indien ze er niet staan zal bij een wijziging van deAccess-Control-Allow-Origin
(bijv. door PHP), de gewijzigde origin er aan toegevoegd worden waardoor er meerdere waarden staan en User Agents laten maar één waarde toe waardoor het niet langer geldig is!
Zie ook:
We gaan een aantal Symfony Bundles installeren:
- FOSRestBundle om een RESTful API te maken;
- JMSSerializerBundle om de Entity's te serializeren;
- NelmioApiDocBundle om de API te documenteren;
- NelmioCorsBundle om CORS te activeren.
$ cd ~/Code/nmdad-iii.arteveldehogeschool.be/www/
$ composer require friendsofsymfony/rest-bundle:@stable jms/serializer-bundle:@stable nelmio/api-doc-bundle:@stable nelmio/cors-bundle:'~1.3'
Daarna moeten we de Bundles registreren in app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// …
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle(),
new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
new Nelmio\CorsBundle\NelmioCorsBundle(),
// …
);
}
}
Voor de NelmioApiDocBundle moeten we nog een route toevoegen aan app/config/routing.yml
:
# …
NelmioApiDocBundle:
resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc
# …
In # app/config/config.yml
:
# Nelmio ApiDocBundle
nelmio_api_doc:
name: "Arteveldehogeschool Blog API documentation"
sandbox:
accept_type: application/json
Test met: http://dev.nmdad-iii.arteveldehogeschool.be/api/doc/
In # app/config/config.yml
:
nelmio_cors:
paths:
'^/api/':
allow_origin: '*'
allow_headers: ['X-Custom-Auth','Content-Type']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE', 'OPTIONS']
max_age: 3600
We bouwen verder op het blogsysteem in Symfony.md
De controllers geven we de naam zoals het meervoud van de Entity Articles
. We maken eerst een kopie van src/Artvelde/ApiBundle/Controller/DefaultController.php
, die we gaan omvormen tot een FOSRestController:
Je kan ook een lijst van routes opvragen via de Symfony-Console met:
vagrant@homestead$ php app/console api:doc:dump
of
vagrant@homestead$ php app/console router:debug | grep 'artevelde_api'
Tip: Routes testen met Nelmio ApiDoc
Bekijk de routes op http://dev.nmdad-iii.arteveldehogeschool.be/api/doc/ en klik daarna op een route en probeer deze in de Sandbox.
...
Zie ook:
Zie ook:
[Johannes Schmitt / Serializer / Reference / Annotations](http://jmsyst.com/libs/serializer/master/reference/annotations)
[Johannes Schmitt / Serializer / Cookbook / Exclusion Strategies](http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies)
Omdat niet alle eigenschappen van de Entity geserialiseerd mag worden, moeten met annotations een aantal dingen aanpassen.
Listeners
Bij een POST
of PUT
wordt een request naar de Controller Action gestuurd. Een eenvoudige manier om een Entity op te vullen met de waarde n uit de request is door de entity te koppelen aan een Form-object en de Request-object te binden aan dat Form-object. Zo krijgt de Entity de waarden uit het form-object en kan eveneens de geldigheid van de gegevens gecontroleerd worden via form validatie.
vagrant@homestead$ php app/console generate:doctrine:form ArteveldeCommonBundle:Article
Verplaats daarna de Form Type klasse naar de ArteveldeApiBundle in een nieuwe map Form\
en vergeet niet de namespace aan te passen met de naam van de nieuwe Bundle.
Tools → Test RESTful Web Service
-
HTTP method:
POST
-
Host/port:
http://dev.nmdad-iii.arteveldehogeschool.be
-
Path:
/api/v1/users/1/articles/
-
Request → +
Name Value Content-Type
application/json
-
Request Body
Text:
{ "article": { "title": "Test Artikel C", "body": "Lorem Ipsum C" } }
-
Druk op het groene Run-icoontje.