REST Factory, simplifier les API REST

Le plugin REST Factory fournit, d’une part, les fonctions REST nécessaires à l’implémentation d’une API REST - dénommée ezrest- conforme à l’organisation imposée par le plugin Serveur HTTP abstrait et permet, d’autre part, une mise en œuvre simplifiée de collections et de ressources au travers de cette API.

Ce plugin est un outil de développement utilisable par d’autres plugins qui souhaitent mettre à disposition, avec le minimum d’effort, leurs données au travers d’une API REST.

L’API REST du plugin « REST Factory »

L’API ezrest

Le plugin REST Factory implémente une API dénommée ezrest conformément au cadre défini par le plugin « Serveur HTTP abstrait ». De fait, les URL sont du type /http.api/ezrest/… et le fichier http/ezrest.php contient l’ensemble des fonctions nécessaires au fonctionnement de l’API.

Pour simplifier la mise en place d’API REST au-delà de ce que propose le plugin Serveur HTTP abstrait, REST Factory définit un cadre standard pour gérer les collections et ressources au travers de son API : vérification, traitement de données et configuration sont standardisés pour convenir à la plupart des besoins et, des points d’entrée spécifiques permettent de personnaliser le fonctionnement proposé par défaut. Les réponses sont toujours fournies au format JSON.

Dans cette version du plugin, seule la méthode GET pour une collection ou une ressource est mise à disposition. Il est aussi possible de récupérer l’index de toutes les collections disponibles.

Les collections

Une collection possède un identifiant unique de type chaine qui en général décrit l’objet au pluriel comme plugins ou categories.

Une collection est fournie par un plugin utilisateur de REST Factory : il déclare sa configuration et propose les services idoines permettant de vérifier les requêtes sur cette collection et d’en récupérer les données.

Les ressources

Une ressource est un objet particulier d’une collection désignée par un identifiant unique qui permet d’accéder à l’objet en base de données : par exemple, l’id pour un article ou le préfixe pour un plugin.

C’est la configuration qui indique si la collection accepte la lecture d’une ressource et si oui au travers de quel identifiant (un plugin peut être identifié par son id ou son préfixe). Il n’est pas toujours utile de proposer l’accès à une ressource précise (dépend des informations disponible sur l’objet). C’est le cas, par exemple, de la collection des catégories.

Les basiques de REST Factory

Pour implémenter une API REST limitée à la méthode GET sur les collections et les ressources, le Serveur HTTP abstrait requiert le codage de la liste des fonctions suivantes :

  • http_ezrest_get_index_dist
  • http_ezrest_get_collection_dist
  • http_ezrest_get_ressource_dist
  • http_ezrest_erreur_dist

Un plugin utilisateur de REST Factory n’aura donc jamais à se préoccuper de ces fonctions, qui sont écrites une fois pour toute. Ces fonctions font appel à des services standardisés pour effectuer les vérifications idoines de chaque requête et constituer la réponse. Certains de ces services sont personnalisables par les plugins utilisateur comme décrit plus avant.

Le plugin REST Factory ne propose aucune collection par défaut mais attend des plugins utilisateur qu’ils déclarent les collections utilisables au travers d’un pipeline nommé liste_ezcollection.

Déclaration des collections

La configuration d’une collection se matérialise par un tableau associatif dont les index sont expliqués ci-dessous.

Paramètres généraux
module Préfixe du plugin fournissant la collection (obligatoire)
cache Tableau de gestion des caches, ce qui revient à définir la méthode de peuplement des données. Voir le bloc « Paramètres du bloc cache » pour le détail.
cle Tableau de définition d’une clé d’autorisation d’accès à l’API (facultatif).
filtres Tableau, éventuellement vide, des filtres autorisés sur la collection. Voir le bloc « Paramètres d’un filtre » pour le détail.
sans_condition Indique si REST Factory doit calculer les conditions SQL associées aux filtres (facultatif).
ressource Champ identifiant la ressource de façon unique dans la table matérialisant la collection et servant à la désigner dans l’url. Par exemple, le préfixe pour les plugins (facultatif).
Paramètres du bloc cache
type Type de cache utilisé (revient à définir la méthode de peuplement des données). Prend les valeurs ’ezrest’ pour la méthode PHP et ’spip’ pour la méthode par squelette.(obligatoire pour un cache).
duree Durée en secondes de conservation du cache (facultatif). Si absent, la durée par défaut des caches REST Factory est utilisée (1 jour).
Paramètres du bloc cle
nom Nom du paramètre d’URL matérialisant la clé d’accès (obligatoire).
format Expression régulière définissant le format de la clé.
Paramètres d’un filtre
critere Nom du filtre dans l’URL. Peut coïncider ou pas au champ dans la table (obligatoire pour un filtre).
est_obligatoire Indique si le filtre doit toujours être utilisé lors d’une requête sur la collection ou pas (facultatif, défaut à false).
vide_interdit Indique si le critère accepte ou pas une valeur vide (facultatif, défaut à false).
sans_condition Indique si REST Factory doit calculer la condition SQL associée au filtre (facultatif, défaut à false).
hors_contenu Indique si le filtre a une influence sur le contenu de la réponse ou pas. Si le filtre a une influence sur le contenu, il est utilisé pour calculer l’identifiant du cache et, en cas de cache spip, il est fourni au contexte du squelette HTML (facultatif, défaut à false).
module Préfixe du plugin fournissant le filtre. Il est en effet possible qu’un plugin utilisateur ne fournisse qu’un filtre supplémentaire dû au fait qu’il rajoute une colonne à la table ou autre. Ce module prévaut sur le module général de la collection (facultatif).
champ_nom Désigne le nom du champ de la table représentant le critère. Ce paramètre ne sert qu’au plugin utilisateur pour construire la condition de lecture de la collection. Omis si le paramètre critère désigne déjà le champ de la table (facultatif).
champ_type Fournit le type PHP du champ, soit ‘string’ ou ‘int’. Cela permet à REST Factory d’utiliser sql_quote() ou intval() pour traiter la valeur dans la condition. Par défaut, le type est considéré comme une chaine (facultatif). Cette configuration est aujourd’hui dépréciée car sql_quote() sait repérer le type du champ concerné.
champ_table Permet à REST Factory de préfixer les champs des conditions SQL par le nom SPIP de la table. Ce champ indique l’objet au pluriel. Si il est omis, REST Factory utilise le nom de la collection et si celui-ci ne mène pas à une table, aucun préfixe n’est ajouté. De même, si il est affecté à une chaine vide aucun préfixe n’est ajouté (facultatif).
format Expression régulière définissant la syntaxe de la valeur du critère (facultatif, défaut à ’’).

L’identifiant de la collection ne fait pas partie du bloc de configuration car il est passé comme index du tableau global de toutes les collections. Cet identifiant est une chaine qui représente en général un type d’objet au pluriel (par exemple, plugins). La déclaration de la collection pays se fait comme suit :

$collections['pays'] = array(
	'module'    => 'isocode',
	'cache'     => array(
		'type'  => 'ezrest',
		'duree' => 3600 * 24 * 30
	),
	'filtres'   => array(
		array(
			'critere'         => 'region',
			'est_obligatoire' => false,
			'champ_nom'       => 'code_num_region',
			'champ_table'     => 'iso3166countries'
		),
		array(
			'critere'         => 'continent',
			'est_obligatoire' => false,
			'champ_nom'       => 'code_continent',
			'champ_table'     => 'iso3166countries'
		),
	),
	'ressource' => 'code_alpha2'
);

Un plugin utilisateur n’est pas obligé de fournir uniquement des collections entières mais peut parfois ne rajouter qu’un filtre. C’est le cas du plugin « SVP Typologie » qui permet de filtrer la collection plugins, fournie par SVP API, sur la catégorie. Dans ce cas, il est nécessaire de s’assurer que la collection de base a bien été déclarée au préalable dans la séquence du pipeline.

Les services de personnalisation de l’API

Pour un plugin utilisateur, on distingue les services liés à une collection et ceux non liés à une collection. C’est cette distinction qui régit le nommage des services du plugin utilisateur.

Les services de personnalisation d’un plugin utilisateur doivent être fournis dans le fichier ezrest/${prefixe}.php, où ${prefixe} désigne le préfixe du plugin utilisateur.

Les services non liés à une collection

Ces services sont tous facultatifs. Il convient à chaque plugin utilisateur de définir l’intérêt de les fournir ou pas. ${prefixe} désigne le préfixe du plugin utilisateur.

${prefixe}_api_verifier_contexte Détermine si le serveur est capable de répondre aux requêtes (par exemple, un contexte de configuration). REST Factory ne fait aucune vérification par défaut.
${prefixe}_reponse_informer_plugin Ajoute des informations au bloc ’fournisseur’ de la réponse
${prefixe}_reponse_expliquer_erreur Permet de modifier le bloc d’erreur après qu’il ait été complètement finalisé. A éviter.

Les services liés à une collection

${collection}_collection_verifier Détermine si la collection demandée est valide et renvoie un bloc d’erreur mis à jour. REST Factory vérifie au préalable que la collection est bien déclarée.
${collection}_verifier_filtre_${critere} Détermine si le filtre de critère ${critere} est valide pour la collection ${collection} et renvoie un bloc d’erreur mis à jour. REST Factory vérifie au préalable les filtres obligatoires et que chaque filtre est bien déclarée
${collection}_verifier_ressource_${champ} Détermine si la ressource identifiée par le champ ${champ} est valide pour la collection ${collection}. REST Factory vérifie au préalable que la collection autorise bien l’accès à une ressource
${collection}_conditionner_${critere} Calcule la condition SQL associée au filtre de critère ${critere} pour la collection ${collection}. REST Factory sait calculer des conditions simples à partir de la configuration du filtre. Cette fonction est à coder uniquement dans des cas complexes.
${collection}_collectionner Renvoie les données en réponse à la requête de lecture de la collection ${collection}. Par défaut, REST Factory ne fait rien, tout est du ressort du plugin utilisateur.
${collection}_ressourcer Renvoie les données en réponse à la requête de lecture d’une ressource de la collection ${collection}. Par défaut, REST Factory ne fait rien, tout est du ressort du plugin utilisateur.

Les méthodes de peuplement des données

Le peuplement de l’index donnees de la réponse peut se faire, soit en PHP au travers des fonctions de service spécifiques du plugin utilisateur ${prefixe}_collectionner() ou ${prefixe}_ressourcer(), soit en utilisant un squelette spécifique appelé par la fonction recuperer_fond() et de chemin ezrest/json/${prefixe}_${collection}.html pour les requêtes de type collection et ressource.

Ce squelette doit construire la chaine JSON valide correspondant aux données spécifiées par la requête et être en capacité de renvoyer la collection ou une ressource de la collection. La chaine JSON est ensuite décodée par REST Factory pour être intégrée dans la réponse.

Le choix de la méthode est définie dans la configuration de la collection.

La gestion des caches

Pour éviter de solliciter la base de données à chaque requête, REST Factory utilise des caches pour stocker pendant une durée limitée les données issues des fonctions PHP de chargement des données ou des squelettes HTML.

Si la requête utilise un squelette HTML pour peupler les données, REST Factory ne fait rien d’autre que de se reposer sur la gestion des caches de SPIP. Si la requête utilise une fonction PHP comme ${prefixe}_collectionner(), REST Factory utilise un cache propre géré par le plugin Cache Factory dont la durée de conservation est définie par la configuration de la collection.

Les caches propres à REST Factory sont stockés dans un sous-répertoire du répertoire local/cache-ezrest/, sous-répertoire au nom du plugin fournisseur de la collection. L’index des collections fournis par REST Factory est stocké dans le sous-répertoire ezrest/. Le nom des caches est composé de deux composants obligatoires, type_requête et collection, et d’une composant facultatif, complement, recevant soit la valeur md5 de la ressource, soit la liste des filtres concaténée et cryptée par md5. Le cache de la collection pays se nomme collection_pays.json et celui de la ressource SVP de la collection plugins ressource_plugins_0fea6a13c52b4d4725368f24b045ca84.json.

REST Factory gère également un index des caches, local/cache-ezrest/index.txt, pour afficher correctement dans le formulaire de vidage la correspondance du md5. Cet index est créé et modifié lors du passage dans le pipeline post_cache.

Cette gestion des caches « serveur » ne doit pas être confondue avec la nécessité, pour les clients, de limiter le nombre de requêtes adressées en gérant de leur côté un cache « client » sous une forme adéquate.

Les pipelines post_ezcollection et post_ezressource

Suite à l’appel des services de récupération des données, ezrest_collectionner() et ezrest_ressourcer(), les fonctions REST de traitement d’un GET sur une collection ou une ressource font appel à un pipeline respectivement post_ezcollection et post_ezressource. Ces pipelines permettent à un plugin de compléter l’ensemble des données récupérées.

La gestion des erreurs

REST Factory simplifie aussi la gestion des erreurs pour les plugins utilisateur. En effet, ceux-ci n’ont jamais à se préoccuper de remplir le bloc d’erreur complet mais se contentent, en général, de fournir un titre, un détail voir le type de l’erreur. Les index modifiables du bloc d’erreur dépendent du service.

Le format des items de langue est imposé par REST Factory et se déduit des index du bloc d’erreur. Seul les index titre et detail du bloc d’erreur sont concernés. Les formats sont les suivants, respectivement pour le titre et le détail :

${prefixe}:erreur_${statut}_${type}_titre
${prefixe}:erreur_${statut}_${type}_message

Pour aller plus loin

Pour aller plus loin dans la compréhension du plugin et de son utilisation, un guide en PDF est fourni avec le code.

Discussion

2 discussions

  • 7
    Holmes Pinto Avila

    J’implémente une API manuellement, en utilisant REACT comme front-end et SPIP comme back-end. Si quelqu’un est intéressé, je peux collaborer à la mise en œuvre.
    J’ai réussi à implémenter les deux et ça a été excellent !

    « Estoy implementado una Api manualmente, utilizando REACT como frond-end, y SPIP como Back-end. Si alguien esta interezado le puedo colaborar con la implementacón.
    Logre implementarla ambos y me ha ido excelente !! »

    • J’avais la même idée pour VueJS. Mais pour l’instant j’ai un problème. Mon API REST fonctionne en local (sur deux postes avec Apache). Mais pas sur le serveur de production (un RPI4 sous proxy NGINX mais je n’ai pas la main sur la conf NGINX). SPIP me renvoie une page 404 alors que les dépôts sont les mêmes (mêmes version de SPIP, mêmes plugins, même BDD). Quelqu’un aurait une idée ?

      https://infojune.fr/http.api/ezrest/

    • Hello,

      As-tu vérifié ton htaccess ?

    • Merci pour ta réponse Eric. J’ai effectivement pensé au .htaccess. Mais j’ai le même fichier sous Windows/Apache que sous Linux/Nginx. Je me suis dit qu’il était peut-être ignoré sous Windows/Apache alors je suis allé voir dedans. A priori, c’est celui d’origine. J’ai essayé de commenter la ligne qui concerne les APIs pour voir mais sans résultat :
      https://pastebin.com/0q87WTtB

    • Ok, non mais la ligne sur l’api est la bonne. Parfois on oublie d’activer le .htaccess à partir du fichier htaccess.txt mais a priori c’est pas le souci.

      J’avoue qu’à distance je ne vois pas ce qui peut poser problème surtout si tu arrives à faire fonctionner l’api en local.

    • En fait, c’est tout simplement que NGINX ne supporte pas les .htaccess ^^
      Ce n’était donc pas l’environnement Windows/Apache qui ignorait le .htaccess mais l’environnement Linux/NGINX.
      Donc la règle à mettre à la racine du vhost dans NGINX :
      rewrite ^/([w] ).api([/.](.*))?$ /spip.php?action=api_$1&arg=$3 last;

    • 4 mois après je rebondis sur ce message.

      Je ne sais pas si la syntaxe change en fonction de la version du server mais cette dernière règle ne fonctionne pas chez moi, en Nginx 1.18

      J’ai un nginx : [emerg] invalid number of arguments in « rewrite » directive qui empêche le serveur de redémarrer ... 🤔

    • OK, c’est résolu. La bonne syntaxe est en réalité la suivante (il faut mettre la ligne dans un bloc location { ... } sinon ça ne fonctionne pas.)

          # redirection des APIs
          location ~ /([\w]+).api([/.](.*))$ {
              rewrite ^/([\w]+).api([/.](.*))?$ /spip.php?action=api_$1&arg=$3 last;
          }

      Du coup j’ai mis à jour le wiki

    Répondre à ce message

  • 3

    Si on crée dynamiquement la liste des collections à exposer dans le pipeline liste_ezcollection (par ex en récupérant la liste des collections via lire_config()), est-ce que ça fonctionnerait bien en cas de modification des collections à exposer ou il faudrait supprimer un cache ou qqe chose du genre ?

    Même question avec des fonctions dynamiques du type :

    <?php
    $collection = "articles";
    $fonction_collectionner = $collection .  "_collectionner";
    $$fonction_collectionner = function($conditions, $filtres, $configuration) {
    	// Initialisation des données de la collection à retourner
    	$contenu = [];
    
    	// Récupération des données de la collection conformément aux filtres éventuellement fournis.
    
    	return $contenu;
    };
    
    $cond = array();
    $filt = array();
    $conf = array();
    $items = $fonction_collectionner ($cond, $filt, $conf);
    ?>
    • Je me demandais d’ailleurs s’il n’aurait pas été plus judicieux d’écrire les fonctions comme ça dans ezREST. Par exemple :

      function collectionner(string $collection, array $conditions, array $filtres, array $configuration) : array {
      	// Initialisation des données de la collection à retourner
      	$contenu = [];
      
      	return $contenu;
      }

      et de splitter tout le fichier prefixe.php dans différents fichiers. Par exemple : prefixe_contexte.php, prefixe_reponses.php, prefixe_verifier.php, prefixe_collectionner.php et prefixe_ressourcer.php

      Au pire, on doit pouvoir au moins splitter le fichier prefixe.php dans le plugin utilisateur grâce à des include non ? Parce qu’il devient vitre très long ce fichier :p

    • Pour définir les collections à exposer vu que cela passe par le pipeline liste_ezcollection on peut lire une configuration meta ou autre, il n’y a pas de souci.

      Pour les fonctions dynamiques faudrait essayer. Ce que je fais c’est que j’appelle dans le code ezREST une fonction d’api xxxx et que c’est elle qui aiguille vers la bonne fonction de service collection_xxxx ou prefixeplugin_xxxx suivant ce que l’on veut. Ces fonctions doivent être dans un include précis ezrest/prefixeplugin.php.

      Sinon, il est toujours possible de splitter l’include des services en plusieurs sous-includes et d’y faire appel dans l’include parent oui.

      Après, si tu as des besoins spécifiques il est toujours possible d’étudier des évolutions du plugin.

    • Autre chose, je te conseille de lire le guide conception qui est inclu dans le plugin « Guide - Le plugin REST Factory.pdf ». Il est plus complet que cet article et donne des exemples d’utilisation.

    Répondre à ce message

Ajouter un commentaire

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparaît.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom