• 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 29/07/2019

Rendez votre API auto découvrable (dernier niveau du modèle de maturité de Richardson)

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Bienvenue dans cette troisième partie !

Nous avons vu dans la première partie qu'il existe une échelle permettant d'établir si une application est RESTful. Il existe 4 niveaux allant de 0 à 3. Dans la partie deuxième partie, il était question de respecter les niveaux 1 et 2 du modèle de maturité de Richardson, en facilitant notre travail de développeur grâce au bundle FOSRestBundle.

Je vous propose maintenant d'améliorer notre application afin d'atteindre l'ultime niveau; le numéro 3, du modèle de maturité de Richardson. Je sens votre excitation ! :D

Votre API expose et manipule des ressources et respecte les niveaux 1 et 2 du modèle de maturité de Richardson. Il est désormais temps d'accéder au niveau 3 : rendre auto découvrable son API.

De quoi s'agit-il ?

Ce niveau 3 reprend le principe de base du Web : les liens. Le fait de pouvoir se balader d'une page à une autre est rendu plus facile lorsque cette page web affiche à l'utilisateur les liens hypertexte cliquables et accessibles à l'internaute. Eh bien, il s'agit de faire exactement la même chose pour votre API ! Nous allons donc fournir aux utilisateurs de notre API les liens hypertexte des ressources qu'il est possible d'obtenir au fur et à mesure.

Commençons par indiquer ce qui est possible de faire avec un article grâce l'API : le modifier et le supprimer. Nous allons donc ajouter les liens pour que les utilisateurs aient accès à l'information directement.

HATEOAS - Hypermedia As The Engine Of Application State

La représentation des informations peut se faire de différentes manières. Des standards ont été mis en place par des collectifs indépendants.

Pour faciliter nos développements, nous allons utiliser une librairie ainsi qu'un bundle qui permet l'intégration de cette librairie. J'ai nommé : BazingaHateoasBundle. Ce bundle intègre la librairie de William DurandHateoas.

Cette librairie implémente le standard HAL.

HAL - Hypertext Application Language

Les spécifications sont disponibles ici : http://stateless.co/hal_specification.html

Il s'agit de respecter le format suivant :

Formalisation des données selon les spécifications HAL
Formalisation des données selon les spécifications HAL(crédit : http://stateless.co/hal_specification.html)

Il s'agit donc d'ajouter les éléments_linkset_embeddedà l'objet JSON représentant notre ressource.

Nous aurons donc :

{
	"id": 1,
	"title": "Titre de mon article.",
	"content": "Contenu de mon article.",
	"_links": {
		"self": {
			"href": "/articles/1"
		},
		"modify": {
		    "href": "/articles/1"
		}
		"delete": {
		    "href": "/articles/1"
		},
	},
	"_embedded": {
		"author": {
			"id": 1,
			"fullname": "Sarah Khalil",
			"biography": "Ma super biographie.",
			"_links": {
				"self": {
					"href": "/author/1"
				}
			}
		}
	}
}

Ce que l'on peut noter :

  • deux éléments dans l'objet JSON avec_links et_embedded;

  • l'élément_linkscontient tous les liens que l'utilisateur est en mesure d'utiliser, en rapport avec la ressource ;

  • l'élément_embeddedcontient tous les éléments connexes à la ressource, ici, l'auteur.

Installer le BazingaHateoasBundle

Commençons donc par installer le bundle qui va nous permettre de débloquer le niveau 3 du modèle de maturité de Richardson.

Tapez la commande suivante :

$ composer require willdurand/hateoas-bundle
Installation du BazingaHateoasBundle
Installation du BazingaHateoasBundle

Il ne nous reste plus qu'à le déclarer dans la classe AppKernel :

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            // …
            new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
        ];

        // …

        return $bundles;
    }
    //…
}

Nous pouvons maintenant commencer à travailler !

Utilisation de base

Ajout de liens : _links

_self

La spécification HAL impose l'ajout de l'élément  _self  dans l'élément_links , avec un lien vers la ressource que l'utilisateur consulte. Pour ce faire, il suffit d'ajouter l'annotation qui suit :

<?php

namespace AppBundle\Entity;

use Hateoas\Configuration\Annotation as Hateoas;
// …

/**
 * …
 * 
 * @Hateoas\Relation(
 *      "self",
 *      href = @Hateoas\Route(
 *          "app_article_show",
 *          parameters = { "id" = "expr(object.getId())" }
 *      )
 * )
 *
 */
class Article
{
    // …
}

Comme vous pouvez le constater, il s'agit simplement de générer l'URL d'accès à la ressource. Le constructeur de la classeHateoas\Configuration\Annotations\Routeprend en paramètres :

  • le nom de la route,

  • les paramètres de la route (uniquement si nécessaire).

Pour récupérer l'id de l'objet courant (la ressource Article), nous nous servons de l'expression language (expr(object.getId())). Il faut donc que la méthodegetId()soit implémentée dans la classeArticle.

Et voici le résultat :

Ajout de l'élément _self à la resource Article
Ajout de l'élément _self à la ressource Article
Ajouter d'autres liens pour indiquer ce qui est possible de faire avec l'API

Il suffit donc d'ajouter les annotations suivantes :

<?php

namespace AppBundle\Entity;

use Hateoas\Configuration\Annotation as Hateoas;

/**
 * …
 * @Hateoas\Relation(
 *      "modify",
 *      href = @Hateoas\Route(
 *          "app_article_update",
 *          parameters = { "id" = "expr(object.getId())" },
 *          absolute = true
 *      )
 * )
 * @Hateoas\Relation(
 *      "delete",
 *      href = @Hateoas\Route(
 *          "app_article_update",
 *          parameters = { "id" = "expr(object.getId())" },
 *          absolute = true
 *      )
 * )
 *
 */
class Article
{
    // …
}

Et voici le résultat :

Ajout des liens pour supprimer et modifier un article
Ajout des liens pour supprimer et modifier un article

Ajouter d'autres ressources : _embedded

Pour les besoins de notre exercice, nous allons ajouter l'auteur de l'article. Pour ce faire, il nous suffit d'ajouter l'annotation suivante :

<?php

namespace AppBundle\Entity;

use Hateoas\Configuration\Annotation as Hateoas;

/**
 * …
 *
 * @Hateoas\Relation(
 *     "author",
 *     embedded = @Hateoas\Embedded("expr(object.getAuthor())")
 * )
 *
 */
class Article
{
    // …
}

Nous voilà avec l'auteur de l'article !

Ajout de l'élément _embedded avec l'auteur de l'article
Ajout de l'élément _embedded avec l'auteur de l'article

Petite astuce

Nous avons vu qu'il est très facile d'aller chercher des informations grâce à la méthode de l'objet courant (ici c'est l'article). Mais que faire si nous avons besoin d'aller chercher l'information à l'extérieur de l'application ?

Eh bien, il est tout à fait possible d'utiliser un service !

Si vous souhaitiez ajouter la météo pour chaque article servi à vos utilisateurs, il faudrait :

  1. créer le service (dans cet exemple,  app.weather) en charge de formater un objet avec les informations à présenter à l'utilisateur ;

  2. faire appel à votre service dans l'une des annotations pour ajouter un nouvel élément à_embeddedgrâce à l'expression language.

    <?php
    
    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Hateoas\Configuration\Annotation as Hateoas;
    use JMS\Serializer\Annotation as Serializer;
    use JMS\Serializer\Annotation\ExclusionPolicy;
    use JMS\Serializer\Annotation\Expose;
    use Symfony\Component\Validator\Constraints as Assert;
    
    /**
     * // …
     * @Hateoas\Relation(
     *     "weather",
     *     embedded = @Hateoas\Embedded("expr(service('app.weather').getCurrent())")
     * )
     *
     */
    class Article
    {
        // …
    }
Ajout de la température courante
Ajout de la température courante

Bien évidemment, cela fonctionne avec tous les services enregistrés dans votre application. Amusez-vous ! :D

NB : Rendez-vous au chapitre Communiquer avec d'autres API pour découvrir le code à produire pour arriver à ce résultat. 

Représentation

Abordons la dernière notion : les représentations.

Nous l'avons vu de manière fortuite lors du TP "Paginer une liste de ressources", vous vous souvenez ? Nous avons créé la classe AppBundle\Representation\Articlespermettant d'accueillir l'ensemble des articles, ainsi que d'autres informations en rapport avec la pagination de la liste.

Une représentation n'est autre qu'une classe  permettant de formater les données différemment de ce qui a été prévu pour l'entité.

La librairie HATEOAS fournit une classe de représentation toute prête pour la pagination ! Superbe ! :soleil: Il s'agit de la classe Hateoas\Representation\PaginatedRepresentation. Cette classe contient déjà toutes les annotations nécessaires pour ajouter tout ce qui est indispensable d'indiquer aux utilisateurs de notre API (nombre d'éléments, page courante, nombre de pages…).

Il vous suffit de suivre les indications du constructeur de la classe pour renvoyer le nouvel objet (PaginatedRepresentation) à sérialiser.

Vous êtes bien évidemment libre de créer autant de classes de représentation que vous le souhaitez.

Rendez-vous au prochain chapitre pour aborder le sujet du versionnement de son API.

Exemple de certificat de réussite
Exemple de certificat de réussite