Upgrade to Pro — share decks privately, control downloads, hide ads and more …

FOSRest 2.0, SymfonyLive Cologne

FOSRest 2.0, SymfonyLive Cologne

SymfonyLive Cologne, 29. April 2016

Christian Flothmann

April 29, 2016
Tweet

More Decks by Christian Flothmann

Other Decks in Programming

Transcript

  1. basecom · wir sprechen internet 1
    FOSRest 2.0
    SymfonyLive, Cologne 2016

    View Slide

  2. basecom · wir sprechen internet 2
    Christian Flothmann
    Software-Entwickler bei basecom in Osnabrück
    - Symfony Core Team
    - Symfony Documentation Team
    - Co-Maintainer FOSRestBundle
    @xabbuh

    View Slide

  3. basecom · wir sprechen internet 3
    Was ist das FOSRestBundle?

    View Slide

  4. basecom · wir sprechen internet 4
    • Routengenerierung
    • Dekodierung von Requestdaten (JSON, XML, etc.)
    • Content-Negotiation
    • Serialisierung von Daten für HTTP-Response
    Tools für

    View Slide

  5. basecom · wir sprechen internet 5
    Warum 2.0?

    View Slide

  6. basecom · wir sprechen internet 6
    • gewachsene Codebasis
    • existierende Extension-Points
    • PHP 5.3
    • Symfony 2.3
    • aufwändig neue Features zu implementieren

    View Slide

  7. basecom · wir sprechen internet 7
    Was gibt es Neues?

    View Slide

  8. basecom · wir sprechen internet 8
    • Code Cleanup
    • Container-Klassenparameter entfernt
    • DI statt Service Locator
    • Update auf willdurand/negotiation 2.0
    • Symfony >= 2.7, PHP >= 5.5.9
    • ParamFetcher und Exception-Handling überarbeitet
    Neu in 2.0

    View Slide

  9. basecom · wir sprechen internet 9
    HTTP-Methoden nach RFC-2518

    View Slide

  10. basecom · wir sprechen internet 10
    • COPY
    • LOCK
    • MKCOL
    • MOVE
    • PROPFIND
    • PROPPATCH
    • UNLOCK
    HTTP-Methoden nach RFC-2518

    View Slide

  11. basecom · wir sprechen internet 11
    FOSRestBundle 1.7
    namespace AppBundle\Controller;
    class UserController
    {
    public function lockUserAction($id)
    {
    }
    }

    View Slide

  12. basecom · wir sprechen internet 12
    FOSRestBundle 1.7
    $ php app/console debug:router lock_user
    +--------------+------------------------------------------------------------------+
    | Property | Value |
    +--------------+------------------------------------------------------------------+
    | Route Name | lock_user |
    | Path | /users/{id}/lock.{_format} |
    | Path Regex | #^/users/(?P[^/]++)/lock(?:\.(?P<_format>json|xml|html))?$#s |
    | Host | ANY |
    | Host Regex | |
    | Scheme | ANY |
    | Method | PATCH |
    | Requirements | _format: json|xml|html |
    | Class | Symfony\Component\Routing\Route |
    | Defaults | _controller: AppBundle:User:lockUser |
    | | _format: NULL |
    | Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
    +--------------+------------------------------------------------------------------+

    View Slide

  13. basecom · wir sprechen internet 13
    FOSRestBundle 1.7
    namespace AppBundle\Controller;
    use FOS\RestBundle\Controller\Annotations\Route;
    class UserController
    {
    /**
    * @Route("/users/{id}", methods={"LOCK"})
    */
    public function lockUserAction($id)
    {
    }
    }

    View Slide

  14. basecom · wir sprechen internet 14
    FOSRestBundle 1.7
    $ php app/console debug:router lock_user
    +--------------+------------------------------------------------------------------+
    | Property | Value |
    +--------------+------------------------------------------------------------------+
    | Route Name | lock_user |
    | Path | /users/{id}.{_format} |
    | Path Regex | #^/users/(?P[^/]++)/lock(?:\.(?P<_format>json|xml|html))?$#s |
    | Host | ANY |
    | Host Regex | |
    | Scheme | ANY |
    | Method | LOCK |
    | Requirements | _format: json|xml|html |
    | Class | Symfony\Component\Routing\Route |
    | Defaults | _controller: AppBundle:User:lockUser |
    | | _format: NULL |
    | Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
    +--------------+------------------------------------------------------------------+

    View Slide

  15. basecom · wir sprechen internet 15
    FOSRestBundle 2.0
    namespace AppBundle\Controller;
    class UserController
    {
    public function lockUserAction($id)
    {
    }
    }

    View Slide

  16. basecom · wir sprechen internet 16
    FOSRestBundle 2.0
    $ php app/console debug:router lock_user
    +--------------+---------------------------------------------------------------+
    | Property | Value |
    +--------------+---------------------------------------------------------------+
    | Route Name | lock_user |
    | Path | /users/{id}.{_format} |
    | Path Regex | #^/users/(?P[^/\.]++)(?:\.(?P<_format>json|xml|html))?$#s |
    | Host | ANY |
    | Host Regex | |
    | Scheme | ANY |
    | Method | LOCK |
    | Requirements | _format: json|xml|html |
    | Class | Symfony\Component\Routing\Route |
    | Defaults | _controller: AppBundle:User:lockUser |
    | | _format: NULL |
    | Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
    +--------------+---------------------------------------------------------------+

    View Slide

  17. basecom · wir sprechen internet 17
    DirectoryRouteLoader

    View Slide

  18. basecom · wir sprechen internet 18
    FOSRestBundle 1.7
    api_category:
    resource: 'AppBundle\Controller\CategoryController'
    type: rest
    api_post:
    resource: 'AppBundle\Controller\PostController'
    type: rest
    api_post_comment:
    resource: 'AppBundle\Controller\PostCommentController'
    type: rest
    api_user:
    resource: 'AppBundle\Controller\UserController'
    type: rest
    # ...

    View Slide

  19. basecom · wir sprechen internet 19
    FOSRestBundle 2.0
    api:
    resource: '@AppBundle/Controller'
    type: rest

    View Slide

  20. basecom · wir sprechen internet 20
    Vorsicht bei sich überlappenden Routen!

    View Slide

  21. basecom · wir sprechen internet 21
    Serializer

    View Slide

  22. basecom · wir sprechen internet 22
    • Abstraktionslayer
    • Adapter für
    Symfony Serializer
    JMS Serializer
    • Extension-Point für eigene Adapter
    • Type-Hinting möglich
    Serializer API

    View Slide

  23. basecom · wir sprechen internet 23
    Serializer API
    namespace FOS\RestBundle\Serializer;
    use FOS\RestBundle\Context\Context;
    interface Serializer
    {
    /**
    * @param mixed $data
    * @param string $format
    * @param Context $context
    *
    * @return string
    */
    public function serialize($data, $format, Context $context);
    // ...
    }

    View Slide

  24. basecom · wir sprechen internet 24
    Serializer API
    namespace FOS\RestBundle\Serializer;
    use FOS\RestBundle\Context\Context;
    interface Serializer
    {
    // ...
    /**
    * @param string $data
    * @param string $type
    * @param string $format
    * @param Context $context
    *
    * @return mixed
    */
    public function deserialize($data, $type, $format, Context $context);
    }

    View Slide

  25. basecom · wir sprechen internet 25
    View-Instanz erzeugen

    View Slide

  26. basecom · wir sprechen internet 26
    1.7: FOSRestController
    namespace AppBundle\Controller;
    use FOS\RestBundle\Controller\FOSRestController;
    use Symfony\Component\HttpFoundation\Response;
    class PostController extends FOSRestController
    {
    public function getPostAction($id)
    {
    if (null === $post = $this->findPost($id)) {
    $view = $this->view(null, Response::HTTP_NOT_FOUND);
    } else {
    $view = $this->view($post);
    }
    return $this->handleView($view);
    }
    }

    View Slide

  27. basecom · wir sprechen internet 27
    1.7: manuell
    namespace AppBundle\Controller;
    use FOS\RestBundle\View\View;
    use Symfony\Component\HttpFoundation\Response;
    class PostController
    {
    // ...
    public function getPostAction($id)
    {
    if (null === $post = $this->findPost($id)) {
    $view = View::create(null, Response::HTTP_NOT_FOUND);
    } else {
    $view = View::create($post);
    }
    return $this->viewHandler->handle($view);
    }
    }

    View Slide

  28. basecom · wir sprechen internet 28
    2.0: Trait
    namespace AppBundle\Controller;
    use FOS\RestBundle\Controller\ControllerTrait;
    use Symfony\Component\HttpFoundation\Response;
    class PostController
    {
    use ControllerTrait;
    public function getPostAction($id)
    {
    if (null === $post = $this->findPost($id)) {
    $view = $this->view(null, Response::HTTP_NOT_FOUND);
    } else {
    $view = $this->view($post);
    }
    return $this->handleView($view);
    }
    }

    View Slide

  29. basecom · wir sprechen internet 29
    API-Versionierung

    View Slide

  30. basecom · wir sprechen internet 30
    1.7: URI
    namespace AppBundle\Controller\Api\V1;
    use FOS\RestBundle\Controller\Annotations\NamePrefix;
    /**
    * @NamePrefix("api_article_v1_")
    */
    class ArticleController
    {
    // ...
    }

    View Slide

  31. basecom · wir sprechen internet 31
    1.7: URI
    namespace AppBundle\Controller\Api\V2;
    use FOS\RestBundle\Controller\Annotations\NamePrefix;
    /**
    * @NamePrefix("api_article_v2_")
    */
    class ArticleController
    {
    // ...
    }

    View Slide

  32. basecom · wir sprechen internet 32
    1.7: URI
    api_article_v1:
    resource: AppBundle\Controller\Api\V1\ArticleController
    type: rest
    prefix: /v1
    api_article_v2:
    resource: AppBundle\Controller\Api\V2\ArticleController
    type: rest
    prefix: /v2

    View Slide

  33. basecom · wir sprechen internet 33
    1.7: URI
    $ php app/console debug:router
    ------------------------------- -------- -------- ------ -----------------------------
    Name Method Scheme Host Path
    ------------------------------- -------- -------- ------ -----------------------------
    api_article_v1_get_articles GET ANY ANY /v1/articles.{_format}
    api_article_v1_get_article GET ANY ANY /v1/articles/{id}.{_format}
    api_article_v1_post_article POST ANY ANY /v1/articles.{_format}
    api_article_v1_put_article PUT ANY ANY /v1/articles/{id}.{_format}
    api_article_v1_delete_article DELETE ANY ANY /v1/articles/{id}.{_format}
    api_article_v2_get_articles GET ANY ANY /v2/articles.{_format}
    api_article_v2_get_article GET ANY ANY /v2/articles/{id}.{_format}
    api_article_v2_post_article POST ANY ANY /v2/articles.{_format}
    api_article_v2_put_article PUT ANY ANY /v2/articles/{id}.{_format}
    api_article_v2_delete_article DELETE ANY ANY /v2/articles/{id}.{_format}
    ------------------------------- -------- -------- ------ -----------------------------

    View Slide

  34. basecom · wir sprechen internet 34
    2.0: Query-Parameter
    fos_rest:
    versioning:
    enabled: true
    resolvers:
    query:
    enabled: true
    parameter_name: version
    GET /articles?version=v2

    View Slide

  35. basecom · wir sprechen internet 35
    2.0: Mime-Type
    GET /articles
    Accept: application/json;version=v1
    fos_rest:
    versioning:
    enabled: true
    resolvers:
    media_type:
    enabled: true
    regex: '/(v|version)=(?P[0-9\.]+)/'
    view:
    mime_types:
    json: ['application/json', 'application/json;version=v1', 'application/json;version=v2']

    View Slide

  36. basecom · wir sprechen internet 36
    2.0: Custom-Header
    fos_rest:
    versioning:
    enabled: true
    resolvers:
    custom_header:
    enabled: true
    header_name: X-Version
    GET /articles
    Accept: application/json
    X-Version: version=v1

    View Slide

  37. basecom · wir sprechen internet 37
    Version als Attribut im Request

    View Slide

  38. basecom · wir sprechen internet 38
    Beispiel: Versionsabhängige Routen
    api_article_v1:
    resource: AppBundle\Controller\Api\V1\ArticleController
    type: rest
    condition: "request.attributes.get('version') == 'v1'"
    api_article_v2:
    resource: AppBundle\Controller\Api\V2\ArticleController
    type: rest
    condition: "request.attributes.get('version') == 'v2'"

    View Slide

  39. basecom · wir sprechen internet 39
    Beispiel: Versionsabhängige Routen
    namespace AppBundle\Controller\Api\V1;
    use FOS\RestBundle\Controller\Annotations\Version;
    /**
    * @Version("v1")
    */
    class ArticleController
    {
    // ...
    }

    View Slide

  40. basecom · wir sprechen internet 40
    Versionsabhängige Controller
    namespace AppBundle\Controller;
    use Symfony\Component\HttpFoundation\Request;
    class ArticleController
    {
    public function getArticlesAction(Request $request)
    {
    if ($request->attributes->get('version', 'v1') === 'v2') {
    // v2-spezifischer Code
    }
    // ...
    }
    }

    View Slide

  41. basecom · wir sprechen internet 41
    REST-Zonen

    View Slide

  42. basecom · wir sprechen internet 42
    • FOSRest-Listener immer aktiv
    • verbrauchen Ressourcen
    • evtl. Konflikt mit anderen Listenern
    REST-Zonen

    View Slide

  43. basecom · wir sprechen internet 43
    • Trennung von API und „normaler“ Anwendung
    • Definition von „REST-Zonen“ anhand von Request-Attributen
    URI-Pfad
    Host
    HTTP-Methode
    IP-Adressen
    REST-Zonen

    View Slide

  44. basecom · wir sprechen internet 44
    REST-Zonen: URI-Pfade
    fos_rest:
    zone:
    - path: '^/api'

    View Slide

  45. basecom · wir sprechen internet 45
    REST-Zonen: Client-IP-Adresse
    fos_rest:
    zone:
    - ips: ['127.0.0.1']

    View Slide

  46. basecom · wir sprechen internet 46
    REST-Zonen: Host
    fos_rest:
    zone:
    - host: 'api.example.com'

    View Slide

  47. basecom · wir sprechen internet 47
    Upgrade

    View Slide

  48. basecom · wir sprechen internet 48
    • 2.0 nicht vollständig abwärtskompatibel
    • Vorbild: Symfony 2.8/3.0
    • 1.8 Release zeitgleich mit 2.0
    • abwärtskompatible Features in 1.8
    • Deprecations für BC-Breaks
    Upgrade-Pfad

    View Slide

  49. basecom · wir sprechen internet 49
    Vielen Dank!
    https://joind.in/talk/efed7

    View Slide