Give me some REST - RESTful APIs mit Symfony

43
GIVE ME SOME REST! RESTFUL APIS MIT SYMFONY Von / Paul Seiffert @seiffertp

description

Webservices werden heute in vielen Bereichen der IT zur Integration unterschiedlicher Anwendungen verwendet. Die Klasse der REST-Webservices spielt dabei eine besondere Rolle, da REST sich auf die Grundlagen von HTTP stützt, einfach verständlich ist und relativ einfach in bestehende Anwendungen zu integrieren ist. Dieser Vortrag gibt einen Überblick über die Herausforderungen einer RESTful API und zeigt, wie diese mit der Hilfe von Symfony einfach gelöst werden können. Diese Slides habe ich für meinen Vortrag auf der DWX 2014 genutzt.

Transcript of Give me some REST - RESTful APIs mit Symfony

Page 1: Give me some REST - RESTful APIs mit Symfony

GIVE ME SOME REST!RESTFUL APIS MIT SYMFONYVon / Paul Seiffert @seiffertp

Page 2: Give me some REST - RESTful APIs mit Symfony

// PAUL SEIFFERTSoftwarearchitekt bei SensioLabs Deutschland GmbH

Page 3: Give me some REST - RESTful APIs mit Symfony

REST?

Page 4: Give me some REST - RESTful APIs mit Symfony

REST!Ressourcen ~ Objekte

HTTP Verben ~ Methoden

Links ~ Assoziationen

Repräsentationen ~ Views

Page 5: Give me some REST - RESTful APIs mit Symfony

ADDRESSIERBARKEITJede Ressource hat eine Adresse (URI)

Beispiel:http://example.com/movies/3

Page 6: Give me some REST - RESTful APIs mit Symfony

UNIFORM INTERFACEMovieCollection

title: StringreleaseDate: Date

Moviecharacter: String

Role

name: stringdateOfBirth: Date

Actor

*

1

1 *

1

*

GET /moviesPOST /moviesGET /movies/1PUT /movies/1DELETE /movies/1

Page 7: Give me some REST - RESTful APIs mit Symfony

GET /movies

HTTP/1.1 200 OKDate: Mon, 14 Jul 2014 12:10:00 GMT

{ "movies": [ { "title": "Indiana Jones and the Temple of Doom", "uri": "/movies/1" }, { "title": "Indiana Jones and the Last Crusade", "uri": "/movies/2" }, { "title": "Indiana Jones and the Temple of the Forbidden Eye", "uri": "/movies/3" } ]}

Page 8: Give me some REST - RESTful APIs mit Symfony

POST /movies

POST /movies HTTP/1.1Content-Type: application/json

{ "movie": { "title": "Indiana Jones and the Kingdom of the Crystal Skull", "releaseDate": "22 May 2008" }}

Page 9: Give me some REST - RESTful APIs mit Symfony

POST /movies

HTTP/1.1 201 CreatedDate: Mon, 14 Jul 2014 12:15:01 GMTLocation: /movies/4

Page 10: Give me some REST - RESTful APIs mit Symfony

GET /movies/1

HTTP/1.1 200 OKDate: Mon, 14 Jul 2014 12:10:00 GMT

{ "movie": { "title": "Indiana Jones and the Temple of Doom", "releaseDate": "22 May 1984" }, "uri": "/movies/1"}

Page 11: Give me some REST - RESTful APIs mit Symfony

PUT /movies/1

PUT /movies/1 HTTP/1.1Content-Type: application/json

{ "movie": { "title": "Indiana Jones and the Temple of Doom", "releaseDate": "23 May 1984" }}

Page 12: Give me some REST - RESTful APIs mit Symfony

PUT /movies/1

HTTP/1.1 204 No ContentDate: Mon, 14 Jul 2014 12:15:01 GMT

Page 13: Give me some REST - RESTful APIs mit Symfony

DELETE /movies/1

HTTP/1.1 204 No ContentDate: Mon, 14 Jul 2014 12:20:00 GMT

Page 14: Give me some REST - RESTful APIs mit Symfony

REST UND SYMFONY?Symfony spricht HTTP (und somit auch REST) fließend!

Page 15: Give me some REST - RESTful APIs mit Symfony

ISN'T THERE A BUNDLE FOR REST??

Page 16: Give me some REST - RESTful APIs mit Symfony

ist super!

... für manche Projekte

FOSRestBundle

Page 17: Give me some REST - RESTful APIs mit Symfony

HERAUSFORDERUNGEN"REST-Syntax"

Abbildung des Domain Models auf Ressourcen

Abbildung der Domain-Logik auf das Uniform Interface

Perfektionistisch sein!

Pragmatisch sein!

Page 18: Give me some REST - RESTful APIs mit Symfony

MUT ZUR EINFACHEN, SAUBEREN LÖSUNG!

Page 19: Give me some REST - RESTful APIs mit Symfony

Request

Application

Content Negotiation

Routing

Content Retrieval / Update

SerializationResponse

Security

Page 20: Give me some REST - RESTful APIs mit Symfony

SECURITY

Notwendiger Weise stateless

Im einfachsten Fall HTTP Basic Authentication

Page 21: Give me some REST - RESTful APIs mit Symfony

CONTENT-NEGOTIATIONGET / HTTP/1.1Host: google.com

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Encoding: gzip,deflate,sdchAccept-Language: en-US,en;q=0.8,de;q=0.6

HTTP/1.1 200 OK

Content-Type: text/htmlContent-Language: enContent-Encoding: gzip

HTTP/1.1 406 Not Acceptable

Page 22: Give me some REST - RESTful APIs mit Symfony

ROUTING

/moviesZeigt auf die Liste der Filme

/movies/1Zeigt auf einen bestimmten Film

Page 23: Give me some REST - RESTful APIs mit Symfony

ROUTING

GET /moviesGibt die Liste der Filme zurück

POST /moviesLegt einen neuen Film an

Page 24: Give me some REST - RESTful APIs mit Symfony

SERIALISIERUNG

Die Content-Negotiation bestimmt das Format

Das Routing bestimmt die Daten

Page 25: Give me some REST - RESTful APIs mit Symfony

SERIALISIERUNG$result = new MovieResult(new Movie('Star Wars: A New Hope', '25 May 1977'));

$serializedContent = $serializer->serialize($result, 'json');

echo $serializedContend;

{ "movie": { "title": "Star Wars: A New Hope", "releaseDate": "25 May 1977" }}

Page 26: Give me some REST - RESTful APIs mit Symfony

Request

Application

Content Negotiation

Routing

Content Retrieval / Update

SerializationResponse

Security

Page 27: Give me some REST - RESTful APIs mit Symfony

UND JETZT MIT SYMFONY!

Page 28: Give me some REST - RESTful APIs mit Symfony

Request

Application

Content Negotiation

Routing

Content Retrieval / Update

SerializationResponse

RequestListener

View Listener

Symfony Routing

Controller / Domain Logic

Security Symfony Security

Page 29: Give me some REST - RESTful APIs mit Symfony

SECURITYsecurity: firewalls: api: pattern: ̂/ http_basic: realm: "My Movie REST API" stateless: true

access_control: - { path: ̂/, roles: ROLE_USER }

Page 30: Give me some REST - RESTful APIs mit Symfony

NOCH MEHR SECURITY…

https://github.com/FriendsOfSymfony/FOSOAuthServerBundle

Page 31: Give me some REST - RESTful APIs mit Symfony

CONTENT NEGOTIATION

https://github.com/willdurand/Negotiation<?php

$negotiator = new \Negotiation\FormatNegotiator();

$acceptHeader = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';$priorities = array('html', 'application/json', '*/*');

$format = $negotiator->getBestFormat($acceptHeader, $priorities);// $format == html

Page 32: Give me some REST - RESTful APIs mit Symfony

... IN EINEM REQUEST-LISTENER:<?php

class FormatListener{ public function onRequest(GetResponseEvent $event) { $request = $event->getRequest();

$format = $this->negotiator->getBestFormat( $request->headers->get('Accept'), $this->availableFormats ); if (null === $format) { $format = $this->defaultFormat; }

$request->attributes->set('_format', $format); }}

Page 33: Give me some REST - RESTful APIs mit Symfony

FÜR SPRACHE UND CHARSET ANALOG.

Page 34: Give me some REST - RESTful APIs mit Symfony

ROUTINGmovies_list: pattern: /movies methods: GET defaults: { _controller: MoviesApiBundle:Movies:get }

movies_add: pattern: /movies methods: POST defaults: { _controller: MoviesApiBundle:Movies:post }

movie_get: pattern: /movies/{id} methods: GET defaults: { _controller: MoviesApiBundle:Movie:get }

movie_put: pattern: /movies/{id} methods: PUT defaults: { _controller: MoviesApiBundle:Movie:put }

movie_delete: pattern: /movies/{id} methods: DELETE defaults: { _controller: MoviesApiBundle:Movie:delete }

Page 35: Give me some REST - RESTful APIs mit Symfony

DER CONTROLLERÜbersetzt aus HTTP-Logik in Applikations-Logik

Erstellt Responses oder gibt angeforderten Daten zurück

Arbeitet (fast) format-agnostisch

Und bitte mit !DTOs

Page 36: Give me some REST - RESTful APIs mit Symfony

<?php

class MoviesController{ public function getAction() { return $this->movieApiService->getMovieList(); }

public function postAction(Request $request) { $movie = $this->serializer->deserialize( $request->getContent(), 'MovieDto', $request->attributes->get('_contentType') );

$this->movieApiService->addMovie($movie);

$response = new Response('', 201); $response->headers->set( 'Location', $this->generateUrl('movie_get', ['id' => $movie->getId()]) );

return $response; }}

Page 37: Give me some REST - RESTful APIs mit Symfony

VOM CONTROLLER ZUM MODELL

Controller Service

Domain Model

DTO Mapper

Validator

Page 38: Give me some REST - RESTful APIs mit Symfony

SERIALISIERUNG https://github.com/schmittjoh/serializer

<?php

$serializer = $container->get('jms_serializer');

$serializedMovie = $serializer->serialize($movie, 'json');$movie = $serializer->deserialize($serializedMovie, 'MovieDto', 'json');

Page 39: Give me some REST - RESTful APIs mit Symfony

SERIALIZER MAPPINGMovieDto: exclusion_policy: all properties: title: expose: true type: string releaseDate: expose: true type: Date

Page 40: Give me some REST - RESTful APIs mit Symfony

<?php

use Symfony\Component\HttpFoundation\Response;use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;

class ResponseSerializationListener{ public function onView(GetResponseForControllerResultEvent $event) { $request = $event->getRequest();

$content = $this->serializer->serialize( $event->getControllerResult(), $request->attributes->get('_format') );

$event->setResponse(new Response($content)); }}

Page 41: Give me some REST - RESTful APIs mit Symfony

FRAGEN?

Page 42: Give me some REST - RESTful APIs mit Symfony

DANKE!

Page 43: Give me some REST - RESTful APIs mit Symfony

LITERATUR

Martin Fowler -

Richardson Maturity Model

Roy Fielding's Dissertation"Architectural Styles and the Design of Network-basedSoftware Architectures"

Patterns of Enterprise ApplicationArchitecture