REST dans le monde Symfony (PHPTour)

REST dans le monde Symfony (PHPTour)

Voici un tour d'horizon des outils disponibles pour créer des APIs REST robustes en PHP et plus précisément avec le framework Symfony2. Les différentes couches en jeu seront passées en revue, que ce soit le routing, la sérialisation, le versioning, les tests, mais également la documentation de vos APIs. Globalement, cette présentation fera l'état des lieux de REST dans le monde Symfony.

Online slides:


William Durand

June 23, 2014


  1. 3.

    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.
  2. 5.

    Level  0  -­‐‑  The  Swamp  of  POX HTTP  as  a

     tunneling  mechanism RPC  style  system  (SOAP,  XML-­‐‑RPC)
  3. 7.
  4. 8.

    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.
  5. 9.

    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
  6. 10.

    Level  3  -­‐‑  Hypermedia  Controls Service  discovery  via  link  relations

    Hypermedia  formats  (ATOM,  HAL,  JSON-­‐‑LD,  etc.)
  7. 12.

    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
  8. 13.

    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)
  9. 14.

    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. <?xml version="1.0" encoding="UTF-8"?> <collection page="1" limit="10" pages="1"> <user id="123"></user> <user id="456"></user> <link rel="self" href="/api/users?page=1&amp;limit=10" /> <link rel="first" href="/api/users?page=1&amp;limit=10" /> <link rel="last" href="/api/users?page=1&amp;limit=10" /> </collection>
  10. 18.

    In  A  Nutshell Integrates  the    library  with  Symfony2 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 JMS  Serializer
  11. 19.

    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; } }
  12. 23.

    In  A  Nutshell Toolbox  of  services  and  listeners  to  build

     RESTful  APIs Generate  HTML,  XML,  JSON  from  a  single  action Automatic  generation  of  routes  from  actions GET  parameter  parsing  and  validation Integration  with  Symfony2  serializer  and  JMS  Serializer Integration  with  SensioFrameworkExtraBundle Accept  header  negotiation  (thx  to  the    lib) Request  body  decoding Negotiation
  13. 24.

    Usage class RestController { /** * Get the list of

    articles * 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) { $articles = array('bim', 'bam', 'bingo'); return new \Acme\MyBundle\MyResponse($articles, $page); } }
  14. 26.

    Content  Type  Negotiation fos_rest: format_listener: rules: - path: ^/ priorities:

    [ html, json, xml ] fallback_format: ~ prefer_extension: true Accept: application/json, application/xml;q=0.9, text/html;q=0.8, text/*;q=0.7, */*;q=0.5
  15. 27.

    Message  Decoding Listener  to  decode  message  body  from  XML,  JSON,

     etc.  into the  request  parameter  bag Integration  with  SensioFrameworkExtraBundle  converters  to turn  parameters  to  objects GET  parameter  validation  and  normalization
  16. 28.

    Allowed  Methods  Listener Examines  all  available  routes  to  determine  what

     HTTP methods  are  available  for  the  request  URI Automatically  sets  an  according  Allow  header  in  the response fos_rest: allowed_methods_listener: true HTTP/1.1 200 OK Allow: GET, POST
  17. 29.

    MIME  Type  Listener Register  MIME  types  that  Symfony2'ʹs  Request  class

     does  not support  by  default fos_rest: view: mime_types: json: ['application/json', 'application/'] rss: 'application/rss+xml' jpg: 'image/jpeg' png: 'image/png' jsonp_handler: ~ p  JSONP  callback  validation  thanks  to  the    library JsonpCallbackValidator
  18. 30.

    Error  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
  19. 32.

    In  A  Nutshell Integrates  the    library  with  Symfony2 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 Hateoas
  20. 33.

    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() {} }
  21. 34.

    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" } } }
  22. 35.

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

    'xml'); <user id="123"> <first_name> </first_name> <last_name> </last_name> <link rel="self" href="/api/users/123"/> </user> <![CDATA[John]]> <![CDATA[Doe]]>
  23. 37.

    RFC  6570:  URI  Template A  compact  sequence  of  characters  for

     describing a  range  of  URIs  through  variable  expansion. URIs URI  Template {username}/
  24. 38.

    Usage demo_route: pattern: /demo/{page} $templateLink = $this ->get('hautelook.router.template') ->generate('demo_route', array(

    'page' => '{page}', 'sort' => '{sort}', 'filter' => array('{filter}'), )); /demo/{page}?{&sort}{&filter%5B%5D*} p  Standalone  library:  TemplatedUriRouter
  25. 40.

    In  A  Nutshell Generates  documentation  for  your  REST  APIs Gathers

     information  from  PHPDoc Supports  FOSRestBundle,  SensioFrameworkExtraBundle, JMSSerializerBundle  and  JMSSecurityExtraBundle annotations Supports  your  own  annotations Allows  to  add  your  own  parsers Sandbox  (Killer  Feature  ♥)
  26. 41.

    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() {}
  27. 42.
  28. 44.

    In  A  Nutshell Response  listener  to  add  Cache-Control  headers Listener

     matches  the  Request  path/host/method/attributes Varnish  helper  class  to  assist  in  purging/banning  content Listener  to  combine  reverse  proxies  with  Symfony2  security Listener  to  move  flash  messages  into  a  cookie
  29. 46.
  30. 47.

    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.­‐‑slides/#slide85
  31. 49.

    FOSOAuthServerBundle Server-­‐‑side  implementation  of  OAuth2 Supports  Doctrine  ORM|ODM,  Propel Highly

     configurable Thank  you    for  helping  us! Alan  Gabriel  Bem  for  OAuth1.0a. BazingaOAuthServerBundle
  32. 50.