Slide 1

Slide 1 text

Architecture REST : On ne va pas se mentir. William Durand - October 30th, 2014 (Cette conférence n'est pas et ne sera jamais sponsorisée par iTélé)

Slide 2

Slide 2 text

Introduction to REST

Slide 3

Slide 3 text

Everyone who has ever talked about REST, at some point, has said something idiotic about REST. (Except for maybe Roy Fielding)

Slide 4

Slide 4 text

REpresentational State Transfer REST is the underlying architectural principle of the web. It is formalized as a set of constraints, described in Roy Fielding's dissertation.

Slide 5

Slide 5 text

Richardson Maturity Model http://martinfowler.com/articles/richardsonMaturityModel.html

Slide 6

Slide 6 text

Level 0 - The Swamp of POX HTTP as a tunneling mechanism RPC style system (SOAP, XML-RPC)

Slide 7

Slide 7 text

Level 1 - Resources Individual resources (URIs) Notion of object identity

Slide 8

Slide 8 text

Level 2 - HTTP Verbs Client uses specific HTTP verbs Server uses HTTP status codes

Slide 9

Slide 9 text

HTTP Verbs Method Safe? Idempotent? GET yes yes HEAD yes yes POST no no PUT no yes DELETE no yes (*) ... no no Safe : means cacheable. Idempotent : result independent on the number of executions.

Slide 10

Slide 10 text

(*) What should be the status code for a DELETE request performed on a non existent resource?

Slide 11

Slide 11 text

HTTP Status Codes Code range Description Example 1xx Information 100 - Continue 2xx Successful 201 - Created 3xx Redirection 301 - Moved Permanently 4xx Client Error 404 - Not Found 5xx Server Error 501 - Not Implemented

Slide 12

Slide 12 text

http://httpstatus.es/

Slide 13

Slide 13 text

Level 3 - Hypermedia Controls Service discovery via link relations Hypermedia formats (ATOM, HAL, JSON-LD, etc.)

Slide 14

Slide 14 text

Level 3 = Content Type Negotiation + HATEOAS

Slide 15

Slide 15 text

Content Type Negotiation Content Type Negotiation is the principle of finding appropriate response formats based on client requirements. No standardized algorithm available Apache algorithm is documented though Also covers encoding (Accept-Encoding) and language (Accept-Language) negotiation mod_negotiation

Slide 16

Slide 16 text

Example Accept: application/json, application/xml;q=0.9, text/html;q=0.8, text/*;q=0.7, */*;q=0.5 Priority Description q=1.0 application/json q=0.9 application/xml q=0.8 text/html q=0.7 text/* (ie. any text) q=0.5 */* (ie. any media type)

Slide 17

Slide 17 text

HATEOAS Hypermedia As The Engine Of Application State. It means that hypermedia should be used to find your way through the API. It is all about state transitions. Your application is just a big state machine.

Slide 18

Slide 18 text

Let's Summarize

Slide 19

Slide 19 text

1 Identify the "things" you want to expose with your API.

Slide 20

Slide 20 text

Collections & Resources GET /programmers { "programmers": [ { "nickname": "willdurand", "powerLevel": 5 } ] } GET /programmers/willdurand { "nickname": "willdurand", "powerLevel": 5 }

Slide 21

Slide 21 text

2 Provide a well-know way of manipulating these "things".

Slide 22

Slide 22 text

Creating Resources POST /programmers HTTP/1.1 Content-Type: application/x-www-form-urlencoded nickname=ubermuda HTTP/1.1 201 Created Location: /programmers/ubermuda Content-Type: application/json { "nickname": "ubermuda", "powerLevel": 0 }

Slide 23

Slide 23 text

Updating Resources PUT /programmers/ubermuda HTTP/1.1 Content-Type: application/json { "nickname": "ubermuda", "powerLevel": 1 } HTTP/1.1 200 OK Content-Type: application/json { "nickname": "ubermuda", "powerLevel": 1 }

Slide 24

Slide 24 text

Deleting Resources DELETE /programmers/ubermuda HTTP/1.1 Idempotent HTTP/1.1 204 No Content Not idempotent HTTP/1.1 404 Not Found

Slide 25

Slide 25 text

3 Link these "things" together so you can navigate from one to another.

Slide 26

Slide 26 text

HAL+JSON GET /programmers { "programmers": [ { "nickname": "willdurand", "powerLevel": 5, "_links": { "self": { "href": "/programmers/willdurand" }, } } ], "_links": { "self": { "href": "/programmers?page=2&limit=1" }, "first": { "href": "/programmers?page=1&limit=1" }, "last": { "href": "/programmers?page=5&limit=1" }, "next": { "href": "/programmers?page=3&limit=1" } } }

Slide 27

Slide 27 text

State Machine

Slide 28

Slide 28 text

4 STATELESS!!!

Slide 29

Slide 29 text

REST is the web!

Slide 30

Slide 30 text

How to build REST APIs with PHP/Symfony?

Slide 31

Slide 31 text

In order to comply with RMM Level 1, I want to turn business objects/data to resources.

Slide 32

Slide 32 text

JMS Serializer ® schmittjoh/serializer

Slide 33

Slide 33 text

In A Nutshell Enables (de) serialization of object graphs Implements visitor pattern to enable flexibility Fully leverage native JSON and XML Custom exclusion strategies to determine what to serialize Quite easy to extend

Slide 34

Slide 34 text

Usage use JMS\SerializerBundle\Annotation as Serializer; /** @Serializer\XmlRoot("response") */ class MyResponse { /** * @Serializer\XmlList(inline=true, entry="article") */ protected $articles; /** * @Serializer\XmlAttribute() */ protected $page; public function __construct(Collection $articles, $page) { $this->articles = $articles; $this->page = $page; } }

Slide 35

Slide 35 text

JSON { "articles": [ "bim", "bam", "bingo" ], "page": "2" }

Slide 36

Slide 36 text

XML bim bam bingo

Slide 37

Slide 37 text

In order to comply with RMM Level 2, I want to use proper HTTP verbs and status codes .

Slide 38

Slide 38 text

But I need to understand what the client wants and says. And I also need to rely on a serializer. And I will have to deal with filters, versioning, etc. I need a framework!

Slide 39

Slide 39 text

FOSRestBundle (+ Symfony) ® FriendsOfSymfony/FOSRestBundle

Slide 40

Slide 40 text

In A Nutshell Toolbox of services and listeners to build RESTful APIs Generate HTML, XML, and JSON from a single action Automatic generation of routes from actions GET parameter parsing and validation Integration with Symfony serializer and JMS Serializer Accept header negotiation (thx to the lib) Request body decoding API versioning Negotiation

Slide 41

Slide 41 text

Usage class RestController { /** * route name: liip_hello_rest_get_articles * pattern: /liip/hello/rest/articles.{_format} * http method requirement: GET * * @View() * @QueryParam(name="page", requirements="\d+", default="1") */ public function getArticlesAction($page) { //$page = $request->query->get('page'); //$page = preg_match('\d+', $page) ? $page : 1; $articles = [ 'bim', 'bam', 'bingo' ]; return new \Acme\MyBundle\MyResponse($articles, $page); } }

Slide 42

Slide 42 text

HTML
bim
bam
bingo
page: 2

Slide 43

Slide 43 text

Message Decoding Decode request body from XML, JSON, etc. into the request Integration with converters to turn parameters to objects GET parameter validation and normalization POST /users HTTP/1.1 Accept: application/json,*/*;q=0.8 Content-Type: application/json {"name":"Don Johnson"} public functon postAction(Request $request) { $name = $request->request->get('name'); }

Slide 44

Slide 44 text

Error/Exception Handling Return correct HTTP status code Determine for which exception to expose the exception message Automatically extract errors from a Form instance fos_rest: exception: codes: 'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404 'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT messages: 'Symfony\Component\Routing\Exception\ResourceNotFoundException': true

Slide 45

Slide 45 text

Want A Smarter Mapping? ® TheBigBrainsCompany/rest-util

Slide 46

Slide 46 text

Usage $invalidArgumentExceptionMapping = new ExceptionMapping(array( 'exceptionClassName' => '\InvalidArgumentException', 'factory' => 'default', 'httpStatusCode' => 400, 'errorCode' => 400101, 'errorMessage' => null, 'errorExtendedMessage' => 'Extended message', 'errorMoreInfoUrl' => 'http://api.my.tld/doc/error/400101', )); $exceptionMap = new ExceptionMap(); $exceptionMap->add($invalidArgumentExceptionMapping); $errorResolver = new ErrorResolver($exceptionMap); $error = $errorResolver->resolve( new \InvalidArgumentException('This is an invalid argument exception.') );

Slide 47

Slide 47 text

Symfony Bundle Configuration tbbc_rest_util: error: exception_mapping: InvalidArgumentException: class: "InvalidArgumentException" factory: default http_status_code: 400 error_code: 400101 error_message: ~ extended_message: "Extended message" more_info_url: "http://api.my.tld/doc/error/400101"

Slide 48

Slide 48 text

Example print_r($error->toArray()); Array ( [http_status_code] => 400 [code] => 400101 [message] => This is an invalid argument exception. [extended_message] => Extended message [more_info_url] => http://api.my.tld/doc/error/400101 ) echo json_encode($error->toArray()); { "http_status_code": 400, "code": 400101, "message": "This is an invalid argument exception.", "extended_message": "Extended message", "more_info_url": "http:\/\/api.my.tld\/doc\/error\/400101" }

Slide 49

Slide 49 text

In order to comply with RMM Level 3, I want to HATEOAS ALL THE THINGS!

Slide 50

Slide 50 text

Hateoas ® willdurand/Hateoas

Slide 51

Slide 51 text

In A Nutshell Leverages the JMS Serializer library Relies on the Symfony2 ExpressionLanguage component Supports JSON and XML Allows to configure links and embedded resources in XML, YAML, PHP, or Annotations Dynamic relations (relation providers) Exclusion strategies

Slide 52

Slide 52 text

Usage use JMS\Serializer\Annotation as Serializer; use Hateoas\Configuration\Annotation as Hateoas; /** * @Serializer\XmlRoot("user") * * @Hateoas\Relation("self", href = "expr('/api/users/' ~ object.getId())") */ class User { /** @Serializer\XmlAttribute */ private $id; private $firstName; private $lastName; public function getId() {} }

Slide 53

Slide 53 text

JSON $hateoas = HateoasBuilder::create()->build(); $json = $hateoas->serialize(new User(123, 'John', 'Doe'), 'json'); { "id": 123, "first_name": "John", "last_name": "Doe", "_links": { "self": { "href": "/api/users/123" } } }

Slide 54

Slide 54 text

XML $hateoas = HateoasBuilder::create()->build(); $xml = $hateoas->serialize(new User(123, 'John', 'Doe'), 'xml');

Slide 55

Slide 55 text

In order to [make humans happy|dominate the world|whatever], I want to create documentation for my API.

Slide 56

Slide 56 text

Swagger UI ® wordnik/swagger-ui

Slide 57

Slide 57 text

NelmioApiDocBundle ® nelmio/NelmioApiDocBundle

Slide 58

Slide 58 text

In A Nutshell Designed for Symfony Generates documentation for your REST APIs Gathers information from PHPDoc Supports FOSRestBundle, SensioFrameworkExtraBundle, JMSSerializerBundle and JMSSecurityExtraBundle Supports your own annotations and you own parsers Sandbox (Killer Feature 쁥) Swagger compliant

Slide 59

Slide 59 text

Usage /** * List all notes. * * @ApiDoc( * resource = true, * statusCodes = { 200 = "Returned when successful" } * ) * @QueryParam( * name="offset", requirements="\d+", nullable=true, * description="Offset from which to start listing notes." * ) * @QueryParam( * name="limit", requirements="\d+", default="5", * description="How many notes to return." * ) */ public function getNotesAction() {}

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

ng-admin ® marmelab/ng-admin

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

Demo?

Slide 64

Slide 64 text

® willdurand/Propilex

Slide 65

Slide 65 text

® gimler/symfony-rest-edition

Slide 66

Slide 66 text

Thank You. Questions? ¾ ® ¬ williamdurand.fr github.com/willdurand twitter.com/couac

Slide 67

Slide 67 text

https://knpuniversity.com/screencast/rest

Slide 68

Slide 68 text

Improving REST in Symfony http://symfony.com/blog/improving-rest-in-symfony https://groups.google.com/forum/#!forum/resting-with-symfony Ä Join us!

Slide 69

Slide 69 text

Further Reading http://www.ietf.org/rfc/rfc2616.txt http://martinfowler.com/articles/richardsonMaturityModel.html http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven http://www.slideshare.net/Wombert/designing-http-interfaces-and-restful-web-services-dpc2012-20120608 http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/ http://www.mnot.net/blog/2012/12/04/api-evolution http://knpuniversity.com/blog/what-the-rest http://knpuniversity.com/blog/rest-revisited http://timelessrepo.com/haters-gonna-hateoas http://blog.liip.ch/archive/2013/10/28/resting-with-symfony2.html http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/ https://blog.apigee.com/detail/restful_api_design_plural_nouns_and_concrete_names

Slide 70

Slide 70 text

Like all great movies, that's not the real end!

Slide 71

Slide 71 text

In order to make my API more fancy self-documenting, I want to use URI Templates.

Slide 72

Slide 72 text

RFC 6570: URI Template A compact sequence of characters for describing a range of URIs through variable expansion. URIs URI Template http://example.com/~fred/ http://example.com/~ {username}/ http://example.com/~mark/

Slide 73

Slide 73 text

TemplatedUriRouter ® hautelook/TemplatedUriRouter

Slide 74

Slide 74 text

Usage demo_route: pattern: /demo/{page} $templateLink = (new Rfc6570Generator($routes)) ->generate('demo_route', array( 'page' => '{page}', 'sort' => '{sort}', 'filter' => array('{filter}'), )); /demo/{page}?{&sort}{&filter%5B%5D*} q Leverages the . Symfony Routing component

Slide 75

Slide 75 text

In order to not burn too many servers, I want to add and manage a caching layer.

Slide 76

Slide 76 text

FOSHttpCache ® FriendsOfSymfony/FOSHttpCache

Slide 77

Slide 77 text

In A Nutshell Set path-based cache expiration headers via app config Set up an invalidation scheme without writing PHP code Tag your responses and invalidate cache based on tags Send invalidation requests with minimal performance impact ( Varnish and Nginx supported out of the box) Differentiate caches based on user type (e.g. roles) Easily implement HTTP cache client

Slide 78

Slide 78 text

In order to provides client applications a secure delegated access to my API on behalf of my users, I want to use OAuth .

Slide 79

Slide 79 text

What Is OAuth? OAuth is an open protocol to allow secure authorization in a simple and standard method from web, mobile, and desktop applications. It is an authorization framework that enables a third-party application to obtain limited access to an HTTP service. http://edu.williamdurand.fr/security-slides/#slide85

Slide 80

Slide 80 text

HWIOAuthBundle Client-side implementation Support 20+ different providers Supports both OAuth1.0a and OAuth2 ® hwi/HWIOAuthBundle

Slide 81

Slide 81 text

FOSOAuthServerBundle Server-side implementation of OAuth2 Supports Doctrine ORM|ODM, Propel Highly configurable Thank you for helping us! Alan Gabriel Bem ® ® (OAuth1.0a) FriendsOfSymfony/FOSOAuthServerBundle willdurand/BazingaOAuthServerBundle

Slide 82

Slide 82 text

The real end.