Bien le bonjour à tous,
Excusez moi pour ma longue absence mais comme Javier l'a fait remarqué il y a quelques jours, j'étais en vacances ce mois de septembre. J'ai pris des vacances aussi bien de mon boulot que de la programmation, voire par moment du net compris (mon dieu que ça fait du bien).
Je pense que nous arrivons doucement au bout de la rédaction de cette ZEP ! Voici un récapitulatif complet de tout ce qui a été dit précédemment sur l'élaboration d'une API des membres (devons-nous renommer le sujet de cette ZEP en ce sens d'ailleurs ?) en incluant les dernières remarques de Javier.
J'indique avec des balises d'information les parties modifiées par rapport au précédent récapitulatif.
API des membres
Informations sur la requête HTTP
Version de l'API
1 | http://api.zestedesavoir.com/v0.0/... |
- La version est indiquée dans l'URL d'appel de l'API.
- L'api est spécifié comme sous-domaine de l'URL pour ne pas la surcharger.
Pour la prod, nous pouvons imaginer une adresse plus ou moins équivalente :
1 | http://api.preprod.zestedesavoir.com/v0.0/... |
User-Agent
Cette section a été modifiée.
L'en-tête HTTP User-Agent
doit être renseigné par le client.
Pour des informations complémentaires sur les User-Agent
, rendez-vous sur son article Wikipedia.
Cross-Origin Request Sharing
Cette section a été modifiée.
Le serveur doit renvoyer les en-tête HTTP suivants afin d'éviter les soucis de CORS :
1 2 3 | Access-Control-Allow-Origin: * Access-Control-Expose-Headers: ETag, Link Access-Control-Allow-Credentials: true |
Pour des informations complémentaires sur CORS, rendez-vous sur son article Wikipedia version anglaise.
Considération générales
Formatage des erreurs
Lorsqu'un appel API provoque une erreur, il faut s'appuyer sur les codes HTTP standards. La réponse HTTP doit donc systématiquement utiliser un code interprétable pour le client. En plus de cela, le contenu de la réponse doit indiquer un code (par défaut le même que celui de la réponse HTTP, mais peut être différents pour donner de plus amples informations) ainsi qu'un message d'erreur compréhensible par le développeur de l'API.
Exemple, une réponse HTTP 404 devrait avoir le body suivant :
1 2 3 4 5 6 7 8 | { errors: [ { code:404, message:"Ce membre n'existe pas" } ] } |
Format d'entrée
- Seul le JSON est accepté.
- Lecture du header
Content-Type
, seule la valeurapplication/json
est acceptée. Les autres formats donnent lieu à un code d'erreur HTTP 406Not Acceptable
. (NB : Twitter fait la différence entre le format des données envoyées et le format dans lequel les données sont envoyées. Dans le cas présent, ils renverraient une 404. A nous de décider ce qu'on fait).
1 2 3 4 5 6 7 8 | { errors: [ { code:406, message:"Le client doit envoyer des données au format application/json et positionner le header HTTP Content-Type de façon à le signaler" } ] } |
Format de sortie
- Uniquement en JSON.
- Lecture du header HTTP
Accept
, si la valeurapplication/json
est absente, envoi d'un code HTTP 406 et de l'erreur suivante :
1 2 3 4 5 6 7 8 | { errors: [ { code:406, message:"Le client doit accepter des réponses au format JSON et le signaler en positionnant le header HTTP Accept avec la valeur application/json" } ] } |
Caching
Cette section a été modifiée.
Pour obtenir un premier système de cache, l'ETag sera utilisé. L'avantage se situe principalement sur un niveau : d'alléger le back-office dans sa charge de travail. Il ne sera pas toujours nécessaire d'aller chercher l'information s'il remarque qu'elle n'a pas été modifiée entre deux appels.
Pour y parvenir, voici le fonctionnement basique d'un ETag :
- Lorsqu'un client fait appel à l'API, la réponse inclue un ETag avec une valeur correspondant à un hash de la donnée retournée par l'API. La valeur de cette ETag est sauvegardée pour une prochaine utilisation si nécessaire.
- Au prochain même appel du client sur l'API, le client peut inclure dans le header de sa requête, l'attribut
If-None-Match
avec le précédent ETag renvoyé par le back-office. - Si l'ETag n'a pas changé, le code de la réponse sera 304 -
Not Modified
et aucune donnée ne sera renvoyée. - Si l'ETAg a changé, le back-office fera la requête et renverra la donnée avec un nouveau ETag. Ce nouveau ETag sera sauvegardé et utilisé pour de prochains appels.
Pagination
-
Les résultats de requête doivent être paginés. Par défaut, une page de résultat doit contenir 50 résultats
-
L'utilisateur indique la page de résultats qu'il souhaite récupérer à l'aide du paramètre
&page=
. Dans le cas où ce paramètre est invalide (n'est pas un nombre entier ou en dehors des limites de la pagination), l'API retourne une erreur 4001 ou 4002 :
1 2 3 4 5 6 7 8 | { errors: [ { code:4001, message:"le paramètre indiqué pour la pagination n'est pas un nombre entier valide" } ] } |
1 2 3 4 5 6 7 8 | { errors: [ { code:4002, message:"la page indiquée n'existe pas" } ] } |
- L'API fournit des indications dans les headers de la réponse HTTP afin d'aider le client à naviguer dans les ressources paginées à l'aide du header Link. de la façon suivante :
1 2 3 4 | Link: <http://zestedesavoir.com/membres/?search=toto&page=4>; rel="next", <http://zestedesavoir.com/membres/?search=toto&page=5>; rel="last", <http://zestedesavoir.com/membres/?search=toto&page=1>; rel="first", <http://zestedesavoir.com/membres/?search=toto&page=2>; rel="prev" |
NB : Ne pas oublier de remettre tous les paramètres utilisés par le client dans l'URL (ici search=
)
- Dans de futures versions, l'utilisateur pourra indiquer le nombre de résultats souhaités en positionnant le paramètre
&perPage=
dans sa requête
Format de dates
Le format de dates retenu pour la communication avec le serveur est le format ISO 8601 (attention Java 7 nécessaire pour les utilisateurs de Java), pas de soucis en Python a priori.
En ce qui concerne les en-têtes HTTP, voici la liste des formats à gérer côté serveur.
Les différentes versions de l'API
Objectif
Fournir aux clients un ensemble d'outils pour consulter la liste des membres et leurs profils.
Représentation d'un membre
Exemple d'objet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { "id": 123456, "username": "Clem", "show_email": true, "email": "clem@zestedesavoir.com", "is_staff": true, "is_active": true, "site": "www.zestedesavoir.com", "avatar_url": "http://i.imgur.com/k6R1plZ.png", "biography": "Quae dum ita struuntur, indicatum est apud Tyrum indumentum regale textum occulte, incertum quo locante vel cuius usibus apparatum. ideoque rector provinciae tunc pater Apollinaris eiusdem nominis ut conscius ductus est aliique congregati sunt ex diversis civitatibus multi, qui atrocium criminum ponderibus urgebantur.", "sign": "", "email_for_answer": true, "last_visit": "1977-04-22T06:00:00Z", "date_joined": "1977-04-22T06:00:00Z" } |
J'ai délibérément omis les champs liés à la modération et spécifique au site comme le survol ou le clique pour le menu.
Colonnes
Colonne | Type | Description |
---|---|---|
id | long | Identifiant de l'utilisateur |
username | String | Nom d'utilisateur |
show_email | boolean | Affiche ou non l'e-mail. Si faux, le champ email ne sera pas spécifié dans l'objet |
String | E-mail de l'utilisateur | |
is_staff | boolean | Est un membre du staff ou non |
is_active | boolean | Est un compte actif ou non |
site | String | Site web de l'utilisateur |
avatar_url | String | Url vers l'avater de l'utilisateur |
biography | String | Biographie de l'utilisateur |
sign | String | Signature de l'utilisateur |
email_for_answer | boolean | Permet l'envoie d'e-mail depuis ZdS |
last_visit | Date | Dernière visite de l'utilisateur sur ZdS |
v0.0 : Afficher un profil utilisateur
Objectif
Mise en place simple du framework et routage sur une URL
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
GET | /v0.0/membres/{id} | Aucun | Renvoie la fiche profil d'un membre | 400 si l'identifiant fourni ne correspond pas au format d'identifiant attendu. 404 si le membre n'existe pas |
v0.1 : Lister les membres
Objectif
Renvoyer une liste au lieu d'un objet.
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
GET | /v0.1/membres/ | Aucun pour le moment, search pour de futures versions | Renvoie la liste des membres sans restriction (pagination ou différentielle) | Aucun autre que les codes standard |
v0.2 : Adaptation/ponts
Cette section a été échangée avec la version suivante.
Objectif
Définition de notre propre Serializer
: On renvoie un modèle différent du modèle stocké en base.
v0.3 : Pagination et différentiel
Cette section a été modifiée.
Objectif
Aller toucher à la requête HTTP en lecture et écriture avec la manipulation des QuerySet
et calcul du ETag.
Pour cette version, l'ETag ne sera pas sauvegardée et sera donc à chaque fois recalculée mais cela permettra d'avoir une version fonctionnelle et testable de l'ETag avant de statuer sur sa persistance.
v0.4 : Gestion de l'authentification.
Cette section a été modifiée.
Objectif
Permettre une authentification OAuth.
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
POST | /v0.4/login/ | Aucun, le login et le passwod est contenu dans la requête | Authentification de l'utilisateur dans l'API | A définir |
GET | /v0.4/membres/monprofil | Aucun | Affiche toutes les données personnelles d'un utilisateur connecté | A définir |
v0.5 : gestion du PUT
Objectifs
Interaction en écriture avec le code côté serveur
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
PUT | /v0.5/membres/ | Aucun, les informations se trouvent en paramètre de la requête | Utilisateur authentifié peut modifier sa fiche profil via l'API | A définir |
v0.6 : gestion du POST
Objectif
Création de compte via l'API membres.
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
POST | /v0.6/membres/ | Aucun, les informations se trouvent en paramètre de la requête | Utilisateur non identifié peut se créer un compte | A définir |
Questions à régler pendant ou après la réalisation de la ZEP
Cette section a été ajoutée.
- Comment sauvegarder l'ETag pour chaque donnée par chaque client ?
- Comment retirer des données de son profil ? Champ null ou vide ?
Futur de l'API des membres (possiblement hors scope de cette ZEP)
Cette section a été modifiée.
v1.x : Gestion des paramètres de requête.
Objectif
Etablir la liaison entre la couche API et la couche d'accès aux données. Que peut-on reprendre du code utilisé actuellement pour les vues ? Peut on factoriser du code pour faire une page de recherche parmi les membres sur le site ?
Routes
Méthode | URL | Paramètres et signification | Description | Codes d'erreur |
---|---|---|---|---|
GET | /v0.7/membres/ | search | Possibilité de rechercher, filtrer, trier la liste des membres | Aucun autre que les codes standard |
v1.x : Gérer les associations dans le modèle renvoyé au client.
Objectif
Aller plus loin que "un modèle en base" <-> "un modèle renvoyé au client".
Ex : extraire un bout (l'url, le nom, …) d'un cours en bêta pour le mettre dans le contenu de la réponse envoyé lors de la consultation de la fiche d'un membre.