Une fois, j'ai aidé un ami qui avait besoin d'intégrer des données sur le logement libre et occupé du système de gestion immobilière avec le site de son client. À ma grande joie, ce système avait une API. Mais malheureusement, cela a été très mal organisé.
J'ai décidé d'écrire cet article non pas pour critiquer le système qui sera discuté, mais pour parler des erreurs rencontrées lors du développement de l'API et suggérer des moyens de corriger ces erreurs.
Aperçu de la situation
L'organisation en question a utilisé le système
Beds24 pour gérer l'espace de vie. Les informations sur ce qui est gratuit et ce qui est occupé ont été synchronisées avec divers systèmes de réservation d'hébergement (tels que Booking, AirBnB et autres). L'organisation était engagée dans le développement du site et souhaitait que la recherche affiche uniquement des informations sur les chambres disponibles pour une période de temps spécifiée et d'une capacité appropriée. Cette tâche semblait très simple, puisque Beds24 fournit une API pour l'intégration avec d'autres systèmes. En fait, il s'est avéré que les développeurs de cette API ont fait beaucoup d'erreurs dans sa conception. Je propose d'analyser ces erreurs, d'identifier des problèmes spécifiques et de parler de la façon d'aborder le développement de l'API dans les situations considérées.
Problème n ° 1: demander le format du corps
Étant donné que le client ne s'intéresse qu'aux informations indiquant si, par exemple, une chambre d'hôtel est libre ou occupée, nous souhaitons uniquement faire référence au
/getAvailabilities
API
/getAvailabilities
. Et, bien qu'un appel à une telle API devrait conduire à des données sur la disponibilité des salles, cet appel ressemble en fait à une demande POST, car l'auteur de l'API a décidé de lui donner la possibilité d'accepter des filtres en tant que corps JSON de la demande. Voici une liste des paramètres de requête possibles et des exemples des valeurs qu'ils acceptent:
{ "checkIn": "20151001", "lastNight": "20151002", "checkOut": "20151003", "roomId": "12345", "propId": "1234", "ownerId": "123", "numAdult": "2", "numChild": "0", "offerId": "1", "voucherCode": "", "referer": "", "agent": "", "ignoreAvail": false, "propIds": [ 1235, 1236 ], "roomIds": [ 12347, 12348, 12349 ] }
Parcourons cet objet JSON et parlons de ce qui ne va pas ici.
- Les dates (
checkIn
, lastNight
et checkOut
) sont au format YYYYMMDD
. Il n'y a absolument aucune raison de ne pas utiliser le format standard ISO 8601 ( YYYY-MM-DD
) lors de la conversion de dates en chaînes, car il s'agit d'une norme largement utilisée pour la présentation des dates. Il est familier à de nombreux développeurs, c'est ce que de nombreux analyseurs JSON s'attendent à recevoir en entrée. En outre, il existe un sentiment que le champ lastNight
est redondant, car il existe un champ checkOut
, qui est toujours représenté par une date un jour avant la date spécifiée dans lastNight
. En ce qui concerne les lacunes notées ci-dessus, je suggère, lors de la conception de telles API, de s'efforcer de toujours utiliser des méthodes standard de présentation des dates et d'essayer de ne pas imposer aux utilisateurs d'API la nécessité de travailler avec des données redondantes. - Tous les champs d'identifiant, ainsi que les
numChild
numAdult
et numChild
, sont numériques, mais sont représentés sous forme de chaînes. Dans ce cas, il n'y a aucune raison apparente de les représenter sous forme de chaînes. - Ici, vous pouvez remarquer les paires de champs suivants:
roomId
et roomIds
, ainsi que propId
et propIds
. La présence des propId
roomId
et propId
est redondante, car les deux peuvent être utilisés pour transmettre des identifiants. De plus, il y a un problème avec les types. Veuillez noter que le champ roomId
est un champ de chaîne et que les valeurs numériques des identifiants doivent être utilisées dans le tableau roomIds
. Cela peut entraîner de la confusion, des problèmes d'analyse et, en outre, suggère que sur le serveur certaines opérations sont effectuées avec des chaînes et d'autres avec des nombres, malgré le fait que ces chaînes et nombres sont utilisés pour représenter les mêmes les données.
Je voudrais inviter les développeurs d'API à essayer de ne pas compliquer la vie de ceux qui utiliseront ces API, tout en faisant des erreurs comme les API lors de la conception des API. À savoir, il faut s'efforcer de mettre en forme des données standard, pour s'assurer qu'elles ne sont pas redondantes, pour s'assurer que différents types de données ne sont pas utilisés pour représenter des entités homogènes. Et ne représentez pas tout sans distinction comme des chaînes.
Problème n ° 2: format du corps de réponse
Comme déjà mentionné, nous ne sommes intéressés que par le
/getAvailabilities
API
/getAvailabilities
. Voyons à quoi ressemble la réponse de ce point final et parlons des lacunes qui ont été commises lors de sa formation. N'oubliez pas que lorsque vous accédez à l'API, nous sommes intéressés par une liste d'identifiants d'objets gratuits pendant une période de temps donnée et pouvant accueillir un nombre donné de personnes. Vous trouverez ci-dessous un exemple du corps de la demande à l'API et un exemple de ce qu'elle renvoie en réponse à cette demande.
Voici la demande:
{ "checkIn": "20190501", "checkOut": "20190503", "ownerId": "25748", "numAdult": "2", "numChild": "0" }
Voici la réponse:
{ "10328": { "roomId": "10328", "propId": "4478", "roomsavail": "0" }, "13219": { "roomId": "13219", "propId": "5729", "roomsavail": "0" }, "14900": { "roomId": "14900", "propId": "6779", "roomsavail": 1 }, "checkIn": "20190501", "lastNight": "20190502", "checkOut": "20190503", "ownerId": 25748, "numAdult": 2 }
Parlez des problèmes de réponse.
- Dans le corps de la réponse, les
numAdult
ownerId
et numAdult
soudainement devenues des nombres. Et dans la demande, il fallait les indiquer sous forme de chaînes. - La liste des objets immobiliers est présentée sous forme de propriétés d'objet dont les clés sont des identifiants de pièce (
roomId
). Il serait logique de s'attendre à ce que ces données soient sorties sous forme de tableau. Pour nous, cela signifie que pour obtenir une liste des pièces disponibles, nous devons roomsavail
sur l'objet entier, tout en vérifiant la présence de certaines propriétés des objets qu'il roomsavail
, comme roomsavail
, et sans prêter attention à quelque chose comme checkIn
et lastNight
. Ensuite, il serait nécessaire de vérifier la valeur de la propriété roomsavail
, et si elle est supérieure à 0, nous pourrions conclure que la propriété correspondante est disponible à la réservation. Voyons maintenant la propriété roomsavail
. Voici les options pour le présenter dans le corps de la réponse: "roomsavail": "0"
et "roomsavail": 1
. Voir le motif? Si les pièces sont occupées, la valeur de la propriété est représentée par une chaîne. S'il est gratuit - il se transforme en nombre. Cela peut entraîner de nombreux problèmes dans les langages strictement liés aux types de données, car la même propriété ne doit pas prendre de valeurs de types différents. En relation avec ce qui précède, je voudrais suggérer aux développeurs d'utiliser des tableaux d'objets JSON pour représenter certains ensembles de données, et de ne pas utiliser de constructions incommodes sous la forme de paires clé-valeur similaires à celle que nous considérons ici. De plus, il est nécessaire de s'assurer que les champs d'objets homogènes ne contiennent pas de données de types différents. Une réponse de serveur correctement formatée pourrait ressembler à celle ci-dessous. Veuillez noter que lors de la présentation des données dans ce formulaire, les informations sur la chambre ne contiennent pas de données en double.
{ "properties": [ { "id": 4478, "rooms": [ { "id": 12328, "available": false } ] }, { "id": 5729, "rooms": [ { "id": 13219, "available": false } ] }, { "id": 6779, "rooms": [ { "id": 14900, "available": true } ] } ], "checkIn": "2019-05-01", "lastNight": "2019-05-02", "checkOut": "2019-05-03", "ownerId": 25748, "numAdult": 2 }
Problème 3: gestion des erreurs
C'est ainsi que la gestion des erreurs est organisée dans l'API considérée ici: le système envoie des réponses avec un code de
200
à toutes les requêtes, même en cas d'erreur. Cela signifie que la seule façon de distinguer une réponse normale d'une réponse avec un message d'erreur est d'analyser le corps de la réponse et de vérifier la présence des champs
error
ou
errorCode
. Seuls les 6 codes d'erreur suivants sont fournis dans l'API.
Codes d'erreur de l'API Beds24Je suggère à tous ceux qui liront ceci de ne pas renvoyer de réponse avec le code 200 (traitement réussi de la demande) si quelque chose s'est mal passé lors du traitement de la demande. Vous ne pouvez effectuer cette étape que si elle est fournie par le framework sur la base duquel vous développez l'API. Le renvoi de codes de réponse adéquats permet aux clients API de savoir à l'avance s'ils doivent analyser le corps de la réponse ou non, et comment le faire (c'est-à-dire, s'il faut analyser une réponse de serveur normale ou un objet d'erreur).
Dans notre cas, il y a deux façons d'améliorer l'API dans cette direction: vous pouvez soit fournir un code HTTP spécial dans la plage 400-499 pour chacune des 6 erreurs possibles (il est préférable de le faire), soit renvoyer, si une erreur se produit, le code 500, qui permettra au moins, le client doit savoir avant d'analyser le corps de la réponse qu'il contient des informations sur l'erreur.
Problème numéro 4: "instructions"
Voici les «instructions» pour utiliser l'API à partir de la documentation du projet:
Veuillez lire les instructions suivantes lorsque vous utilisez l'API.
- Les appels d'API doivent être conçus de telle sorte que lors de leur exécution, ils doivent envoyer et recevoir un minimum de données.
- Les appels d'API sont effectués un à la fois. Vous devez attendre le prochain appel à l'API avant de passer le prochain appel.
- Si vous devez effectuer plusieurs appels à l'API, une pause de quelques secondes doit être prévue entre eux.
- Les appels d'API ne doivent pas être effectués trop souvent, maintenant le niveau d'appels au niveau minimum nécessaire pour résoudre les tâches client.
- Une utilisation excessive de l'API dans un délai de 5 minutes entraînera la suspension de votre compte sans notifications supplémentaires.
- Nous nous réservons le droit de bloquer l'accès au système aux clients qui, à notre avis, utilisent excessivement l'API. Cela se fait à notre discrétion et sans préavis.
Bien que les points 1 et 4 semblent tout à fait justifiés, je ne suis pas d'accord avec les autres points de cette instruction. Considérez-les.
- Numéro d'article 2. Si vous développez une API REST, il est supposé que ce sera une API indépendante de l'état. L'indépendance des appels API par rapport aux appels précédents est l'une des raisons pour lesquelles la technologie REST a trouvé une large application dans les applications cloud. Si un certain module du système ne prend pas en charge l'état, il peut être facilement redéployé en cas d'erreur. Les systèmes basés sur de tels modules évoluent facilement lorsque la charge sur eux change. Lors de la conception d'une API RESTful, vous devez vous assurer qu'il s'agit d'une API indépendante de l'état et que ceux qui l'utilisent n'ont pas à se soucier de quelque chose comme exécuter une seule demande à la fois.
- Numéro d'article 3. Cet article semble plutôt étrange et ambigu. Je ne comprends pas la raison pour laquelle ce paragraphe de l'instruction a été écrit, mais j'ai l'impression qu'il nous dit que dans le processus de traitement de la demande, le système effectue certaines actions et s'il est "distrait" par une autre demande, pas envoyée à temps, cela peut interférer avec son travail. De plus, le fait que l'auteur du manuel dise «quelques secondes» ne nous permet pas de connaître la durée exacte de la pause qui doit être maintenue entre les demandes successives.
- Points n ° 5 et n ° 6. Il fait référence à «une utilisation excessive de l'API», mais aucun critère pour une «utilisation excessive» n'est donné. Peut-être que c'est 10 requêtes par seconde? Ou peut-être 1? De plus, certains projets Web peuvent générer d'énormes quantités de trafic. Si, sans raison adéquate et sans notification, ils ferment l'accès à l'API dont ils ont besoin, leurs administrateurs refuseront très probablement d'utiliser ces API. Si vous écrivez de telles instructions, utilisez un langage clair et mettez-vous à la place des utilisateurs qui doivent travailler avec votre système, guidés par vos instructions.
Problème numéro 5: documentation
Voici à quoi ressemble la documentation de l'API.
Documentation de l'API Beds24Le seul problème avec cette documentation est son apparence. Il serait beaucoup mieux s'il était bien formaté. Surtout afin de montrer l'apparence possible d'une telle documentation, j'ai, en utilisant
Dillinger , et y consacrant moins de deux minutes, en faire la version suivante. À mon avis, cela semble beaucoup mieux que ce qui précède.
Documentation amélioréePour créer de tels matériaux, il est recommandé d'utiliser des outils spéciaux. Si nous parlons de documents simples similaires à celui décrit ci-dessus, alors quelque chose comme un fichier de démarque ordinaire est tout à fait suffisant pour leur conception. Si la documentation est plus compliquée, alors pour sa conception, il est préférable d'utiliser des outils comme
Swagger ou
Apiary .
Soit dit en passant, si vous souhaitez consulter la documentation de l'API Beds24, jetez un œil
ici .
Problème 6: Sécurité
La documentation de tous les points de terminaison API indique ce qui suit:
Pour utiliser ces fonctions, l'accès à l'API doit être autorisé. Cela se fait dans le menu PARAMÈTRES → COMPTE → ACCÈS AU COMPTE.Cependant, en réalité, n'importe qui peut accéder à cette API et, à l'aide de certains appels, en obtenir des informations sans fournir d'informations d'identification. Par exemple, cela s'applique également aux requêtes concernant la disponibilité de certains logements. Ceci est discuté dans une autre partie de la documentation.
La plupart des méthodes JSON nécessitent une clé API pour accéder à un compte. La clé d'accès API peut être définie à l'aide du menu PARAMÈTRES → COMPTE → ACCÈS AU COMPTE.En plus de l'explication incompréhensible des problèmes d'authentification, il s'avère que l'utilisateur doit créer lui-même la clé pour accéder à l'API (cela se fait, en passant, en remplissant manuellement le champ correspondant, certains moyens pour créer automatiquement des clés ne sont pas fournis). La longueur de clé doit être comprise entre 16 et 64 caractères. Si vous autorisez les utilisateurs à créer leurs propres clés pour accéder à l'API, cela peut entraîner l'apparition de clés très peu sécurisées qui peuvent être facilement récupérées. Dans une situation similaire, des problèmes liés au contenu des clés sont possibles, car vous pouvez entrer n'importe quoi dans le champ clé. Dans le pire des cas, cela pourrait conduire à une attaque sur le service en utilisant l'injection SQL ou quelque chose comme ça. Lors de la conception d'une API, n'autorisez pas les utilisateurs à créer eux-mêmes des clés pour accéder à l'API. Au lieu de cela, générez-leur automatiquement des clés. L'utilisateur ne doit pas être en mesure de modifier le contenu d'une telle clé, mais, si nécessaire, il doit pouvoir générer une nouvelle clé, reconnaissant l'ancienne comme non valide.
Dans le cas des demandes qui nécessitent une authentification, nous voyons un autre problème. Il consiste en ce qu'un jeton d'authentification doit être envoyé dans le cadre du corps de la requête. Voici comment cela est décrit dans la documentation.
Exemple d'authentification de l'API Beds24Si le jeton d'authentification est transmis dans le corps de la demande, cela signifie que le serveur devra analyser le corps de la demande avant qu'il n'atteigne la clé. Après cela, il extrait la clé, effectue l'authentification, puis décide - que doit-il faire de la demande - de l'exécuter ou non. Si l'authentification réussit, le serveur ne sera pas soumis à une charge supplémentaire, car dans ce cas, le corps de la demande devra encore être analysé. Mais si la demande n'a pas pu s'authentifier, un temps processeur précieux sera consacré à l'analyse du corps de la demande pour rien. Il serait préférable d'envoyer un jeton d'authentification dans l'en-tête de la demande, en utilisant quelque chose comme un schéma d'authentification au
porteur . Avec cette approche, le serveur devra analyser le corps de la requête uniquement si l'authentification réussit. Une autre raison pour laquelle nous recommandons d'utiliser un schéma standard comme Bearer pour l'authentification est le fait que la plupart des développeurs connaissent ces schémas.
Problème numéro 7: performances
C'est le dernier numéro de ma liste, mais il n'enlève rien à son importance. Le fait est qu'il faut un peu plus d'une seconde pour terminer la demande à l'API en question. Dans les applications modernes, de tels retards peuvent être inacceptables. En fait, ici, vous pouvez conseiller à toutes les personnes impliquées dans le développement de l'API de ne pas oublier les performances.
Résumé
Malgré tous les problèmes dont nous parlions ici, l'API en question nous a permis de résoudre les problèmes rencontrés par le projet. Mais les développeurs ont pris beaucoup de temps pour comprendre l'API et implémenter tout ce dont ils ont besoin. De plus, pour résoudre des problèmes simples, ils ont dû écrire du code assez compliqué. Si cette API était conçue comme il se doit, le travail se ferait plus rapidement et la solution clé en main serait plus simple.
Par conséquent, je voudrais demander à tous ceux qui conçoivent l'API de réfléchir à la façon dont les utilisateurs de leurs services travailleront avec elle. Assurez-vous que la documentation de l'API décrit pleinement leurs capacités, afin qu'elle soit compréhensible et bien conçue. Contrôlez la dénomination des entités, assurez-vous que les données que votre API émet ou reçoit sont clairement structurées afin qu'il soit facile et pratique de travailler avec elles. De plus, n'oubliez pas la sécurité et le traitement correct des erreurs. Si nous prenons en compte tout ce dont nous avons parlé lors de la conception de l'API, vous n'avez pas besoin d'écrire quelque chose comme les étranges «instructions» dont nous avons discuté ci-dessus pour travailler avec.
Comme déjà mentionné, ce matériel n'est pas destiné à décourager les lecteurs d'utiliser Beds24 ou tout autre système avec une API mal conçue. Mon objectif était de montrer des exemples d'erreurs et d'approches à leur solution, de donner des recommandations, à la suite desquelles chacun pourrait améliorer la qualité de ses développements. J'espère que ce matériel attirera l'attention des programmeurs qui le liront sur la qualité des solutions qu'ils développent. Cela signifie qu'il y aura plus de bonnes API dans le monde.
Chers lecteurs! Avez-vous rencontré des API mal conçues?