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

PHP Benelux - Build Restful API easily with Symfony

PHP Benelux - Build Restful API easily with Symfony

Let's build a nice REST API with all the recent helper we can find. First of all, we will take some time to see, in theory, what's asked to build a "good" Restfull API.
Then we'll see some code with the usage of :
FOSRestBundle, and save a lot of time;
JMS serializer and its awesome features;
the HATEOAS project, very nice to unlock the third level of the Richardson maturity model;
And finaly, a little bit of guzzle to ease communication between applications (going further with the SOA architecture).
The goal of that talk is to demystify all technologies in the "REST" galaxy.

Sarah KHALIL

January 29, 2016
Tweet

More Decks by Sarah KHALIL

Other Decks in Technology

Transcript

  1. Who am I? • Head of • Trainer & Developer

    • Enjoying sharer Sarah Khalil @saro0h
  2. What is REST? • REpresentational State Transfer • Architecture (not

    a protocol!) • Associated to Service Oriented Architecture • Management of resources
  3. Uniform interface (5/6) Each resource has its identifier: the URI.

    Each resource has its representation. Auto-descripted message: for instance, JSON = content-type header.
  4. Code on demand (6/6) The client download some code and

    executes it The server is not aware of what is happening anymore optional
  5. HTTP Methods • GET • POST • PUT • PATCH

    • DELETE • OPTIONS • CONNECT • HEAD
  6. Keep’em in mind JSON, XML, HTML… By developers and for

    developers Scalability Latency Security !
  7. class DefaultController { public function indexAction() { $obj = new

    \StdClass(); $obj->latitude = -33.87087; $obj->longitude = 151.225459; $data = $this->get(‘jms_serializer’) ->serialize($obj, ’json’); return new Response( $data, 200, array(‘Content-Type’=>’application/json’) ); } }
  8. Exclusion policy /** * @JMS\ExclusionPolicy("all") */ class Product { /**

    * @JMS\Expose() */ private $name; private $users; } • Visitor pattern • Object graph • Relations (xToMany)
  9. public static function getSubscribingMethods() { return [ [ 'direction' =>

    GraphNavigator::DIRECTION_SERIALIZATION, 'format' => ‘json', 'type' => ‘AppBundle\MyClass’, 'method' => ‘serialize', ], [ 'direction' => GraphNavigator::DIRECTION_DESERIALIZATION, 'format' => ‘json', 'type' => ‘AppBundle\MyClass', 'method' => ‘deserialize', ], ]; }
  10. public function serialize( JsonSerializationVisitor $visitor, MyClass $object, array $type, Context

    $context ) { $data = $object->toArray(); return $data; } public function deserialize(JsonDeserializationVisitor $visitor, $data) { $object = new AppBundle\MyClass($data); return $object; }
  11. class PostSerializeMediaListener implements EventSubscriberInterface { public function onPostSerialize(ObjectEvent $event) {

    $myObject = $event->getObject(); $event->getVisitor()->addData('newElementInJson', 'information’); } public static function getSubscribedEvents() { return array( array( 'event' => Events::POST_SERIALIZE, 'format' => ‘json', 'class' => ‘AppBundle\MyClass', 'method' => ‘onPostSerialize', ), ); } }
  12. Controller # app/config/config.yml fos_rest: view: view_response_listener: force sensio_framework_extra: view: {

    annotations: false } /** * @View() */ public function getUsersAction() { … return $data; }
  13. Content-Type negociation • Request => Accept priorities of media-type(s) that

    your application is ready to work with. • Response => Content-Type the media type of the response (html, json, png…).
  14. Request body converter Decoder ParamConverter /** * @ParamConverter("post", converter="fos_rest.request_body") */

    public function updateAction(Post $post) { } # app/config/config.yml sensio_framework_extra: request: { converters: true } fos_rest: body_converter: enabled: true
  15. Validation (with the Symfony Validator component) Configuration fos_rest: body_converter: enabled:

    true validate: true validation_errors_argument: validationErrors
  16. Validation (with the Symfony Validator component) Usage /** * @ParamConverter("post",

    converter="fos_rest.request_body") */ public function putPostAction(Post $post, ConstraintViolationListInterface $validationErrors) { if (count($validationErrors) > 0) { // Handle validation errors } }
  17. /** * @QueryParam( * name="sort", * requirements="(asc|desc)", * allowBlank=false, *

    default="asc", * description="Sort direction" * ) * @RequestParam( * name="firstname", * requirements="[a-z]+" * ) * @QueryParam( * array=true, * name="filters", * requirements=@MyComplexConstraint * ) */ public function getArticlesAction(ParamFetcher $paramFetcher) {} http://mydomain.com/resources?sort=desc In $request->request ($_POST) fos_rest.param_fetcher_listener: true
  18. fos_rest: view: exception_wrapper_handler: My\Bundle\Handler\MyExceptionWrapperHandler Take example on the FOS\RestBundle\Util\ExceptionWrapper 1

    implements FOS\RestBundle\ViewExceptionWrapperHandlerInterface 2 public function wrap($data) { return new MyExceptionWrapper($data); } 3 Error handling Option 2
  19. Other features • Automatic generation of routes • Consistency •

    Versioning • url • mime type And way more! http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
  20. HAL • Hypertext Application Language • _links - relations •

    _embedded - related resource(s) • self • Specifications: http://stateless.co/hal_specification.html
  21. JSON API • Similar to HAL • meta - meta

    info such as pagination… • links - Relations (xToOne - xToMany) • linked - Resource(s) linked • Specifications : http://jsonapi.org/format/
  22. /** * @Hateoas\Relation( * "self", * href = @Hateoas\Route( *

    "get_notification", * absolute= true, * parameters = { * "id" = "expr(object.getId())", * "user" = "expr(service(‘security.context’).getToken().getUser().getUuid())" * } * ) * ) */ class Notification Expression Language
  23. /** * … * @Hateoas\Relation( * "activity", * href =

    "expr(‘/activity/'~object.getActivity().getId()", * embedded = "expr(object.getActivity())", * exclusion = @Hateoas\Exclusion(excludeIf = "expr(object.getActivity() === null)") * ) */ class Notification
  24. Result { "id": 1, "_embedded": { "activity": { "id": 3,

    "content": "Activity content", "_links": { "self": { "href": "/activities/3" } } } } }
  25. Representation Use representation to decorate your resource Hateoas\Representation\PaginatedRepresentation And many

    more: https://github.com/willdurand/Hateoas/tree/master/src/Hateoas/Representation
  26. class CollectionRepresentation { private $resources; public function __construct($resources) { $this->resources

    = $resources; } public function getResources() { return $this->resources; } }
  27. "page": 1, "limit": 10, "pages": "100", "total": 1000, "_links": {

    "self": { "href": "http://domain.name/notifcations?page=1&limit=10" }, "first": { "href": "http://domain.name/notifcations?page=1&limit=1" }, "last": { "href": "http://domain.name/notifcations?page=100&limit=1" }, "next": { "href": "http://domain.name/notifcations?page=2&limit=1" } }
  28. Guzzle Bundle HTTP Client Request Response HTTPS Error management •

    https://github.com/misd- service-development/guzzle- bundle • https://github.com/csarrazi/ CsaGuzzleBundle
  29. Workflow 1. The user authenticates [him|her]self on frontend 1 2.

    Do some stuff 3. Then clicks on a link that brings him/ her to the frontend 2
  30. Service 1 Service 2 Client Frontend 2 Frontend 1 Service

    3 Service 4 GET /admin HTTP/1.1 User-Agent: curl/7.37.1 Host: 127.0.0.1:8000 Accept: */* Authorization: Bearer XXXXXX
  31. Authentication • Authorization header: Authorization: Bearer <access_token> • https://github.com/poledev/katas/tree/kata- authentication

    • http://symfony.com/doc/current/cookbook/security/ api_key_authentication.html • Make it stateless!