Partager via


Meilleures pratiques pour la conception de l’API web RESTful

Une implémentation de l’API web RESTful est une API web qui utilise des principes architecturaux REST (Representational State Transfer) pour obtenir une interface sans état, faiblement couplée entre un client et un service. Une API web qui est RESTful prend en charge le protocole HTTP standard pour effectuer des opérations sur les ressources et retourner des représentations des ressources qui contiennent des liens hypermedia et des codes d’état d’opération HTTP.

Une API web RESTful doit s’aligner sur les principes suivants :

  • Indépendance de la plateforme, ce qui signifie que les clients peuvent appeler l’API web indépendamment de l’implémentation interne. Pour atteindre l’indépendance de la plateforme, l’API web utilise HTTP comme protocole standard, fournit une documentation claire et prend en charge un format d’échange de données familier tel que JSON ou XML.

  • Couplage libre, ce qui signifie que le client et le service web peuvent évoluer indépendamment. Le client n’a pas besoin de connaître l’implémentation interne du service web, et le service web n’a pas besoin de connaître l’implémentation interne du client. Pour obtenir un couplage libre dans une API web RESTful, utilisez uniquement des protocoles standard et implémentez un mécanisme qui permet au client et au service web d’accepter le format des données à échanger.

Cet article décrit les meilleures pratiques pour la conception d’API web RESTful. Il couvre également les modèles de conception courants et les considérations relatives à la création d’API web qui sont faciles à comprendre, flexibles et gérables.

Concepts de conception de l’API web RESTful

Pour implémenter une API web RESTful, vous devez comprendre les concepts suivants.

  • URI (Uniform Resource Identifier) : Les API REST sont conçues autour des ressources, qui sont tout type d’objet, de données ou de service auquel le client peut accéder. Chaque ressource est représentée par un URI qui identifie de façon unique cette ressource. Par exemple, l’URI d’une commande client particulière peut être :

    https://api.contoso.com/orders/1
    
  • La représentation des ressources définit la façon dont une ressource identifiée par son URI est encodée et transportée sur le protocole HTTP dans un format spécifique, tel que XML ou JSON. Les clients qui souhaitent récupérer une ressource spécifique doivent utiliser l’URI de la ressource dans la demande à l’API. L’API retourne une représentation de ressource des données que l’URI indique. Par exemple, un client peut effectuer une requête GET à l’identificateur https://api.contoso.com/orders/1 d’URI pour recevoir le corps JSON suivant :

    {"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}
    
  • L’interface uniforme est la façon dont les API RESTful obtiennent un couplage libre entre les implémentations client et de service. Pour les API REST basées sur HTTP, l’interface uniforme inclut l’utilisation de verbes HTTP standard pour effectuer des opérations telles que GET, , POSTPUT, PATCHet DELETE sur des ressources.

  • Modèle de requête sans état : Les API RESTful utilisent un modèle de requête sans état, ce qui signifie que les requêtes HTTP sont indépendantes et peuvent se produire dans n’importe quel ordre. Pour cette raison, la conservation des informations d’état temporaires entre les requêtes n’est pas réalisable. Le seul endroit où les informations sont stockées se trouve dans les ressources elles-mêmes, et chaque requête doit être une opération atomique. Un modèle de requête sans état prend en charge une scalabilité élevée, car il n’a pas besoin de conserver d’affinité entre les clients et les serveurs spécifiques. Toutefois, le modèle sans état peut également limiter l’extensibilité en raison de défis liés à l’extensibilité du stockage back-end du service web. Pour plus d’informations sur les stratégies de scale-out d’un magasin de données, consultez Partitionnement des données.

  • Liens Hypermedia : Les API REST peuvent être pilotées par des liens hypermedia contenus dans chaque représentation de ressource. Par exemple, le bloc de code suivant montre une représentation JSON d’un ordre. Il contient des liens pour récupérer ou actualiser le client associé à la commande.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://api.contoso.com/customers/3", "action":"PUT" }
      ]
    }
    

Définir des URI de ressource d’API web RESTful

Une API web RESTful est organisée autour des ressources. Pour organiser la conception de votre API autour des ressources, définissez des URI de ressources qui mappent aux entités métier. Dans la mesure du possible, basez les URI des ressources sur des noms (la ressource) et non sur des verbes (les opérations sur la ressource).

Par exemple, dans un système de commerce électronique, les entités commerciales principales peuvent être des clients et des commandes. Pour créer une commande, un client envoie les informations de commande dans une requête HTTP POST à l’URI de ressource. La réponse HTTP à la requête indique si la création de l’ordre réussit.

L’URI de création de la ressource d’ordre peut être semblable à ceci :

https://api.contoso.com/orders // Good

Évitez d’utiliser des verbes dans des URI pour représenter des opérations. Par exemple, l’URI suivant n’est pas recommandé :

https://api.contoso.com/create-order // Avoid

Les entités sont souvent regroupées dans des collections telles que des clients ou des commandes. Une collection est une ressource distincte des éléments de la collection. Elle doit donc avoir son propre URI. Par exemple, l’URI suivant peut représenter la collection de commandes :

https://api.contoso.com/orders

Une fois que le client a récupéré la collection, il peut effectuer une requête GET à l’URI de chaque élément. Par exemple, pour recevoir des informations sur une commande spécifique, le client effectue une requête HTTP GET sur l’URI https://api.contoso.com/orders/1 pour recevoir le corps JSON suivant en tant que représentation de ressources des données de commande interne :

{"orderId":1,"orderValue":99.9,"productId":1,"quantity":1}

Conventions de nommage des URI de ressources

Lorsque vous concevez une API web RESTful, il est important d’utiliser les conventions de nommage et de relation appropriées pour les ressources :

  • Utilisez des noms pour les noms de ressources. Utilisez des noms pour représenter des ressources. Par exemple, utilisez /orders au lieu de /create-order. Les méthodes HTTP GET, POST, PUT, PATCH et DELETE impliquent déjà l’action verbale.

  • Utilisez des noms pluriels pour nommer des URI de collection. En général, il permet d’utiliser des noms pluriels pour les URI qui référencent des collections. Il est recommandé d’organiser des URI pour les collections et les éléments dans une hiérarchie. Par exemple, /customers est le chemin d’accès à la collection du client et /customers/5 est le chemin d’accès au client avec un ID égal à 5. Cette approche permet de garder l’API web intuitive. En outre, de nombreux frameworks d’API web peuvent router les demandes en fonction des chemins d’URI paramétrés, afin de pouvoir définir un itinéraire pour le chemin d’accès /customers/{id}.

  • Tenez compte des relations entre différents types de ressources et la façon dont vous pouvez exposer ces associations. Par exemple, /customers/5/orders peut représenter toutes les commandes du client 5. Vous pouvez également aborder la relation dans l'autre sens en représentant l'association d'une commande vers un client. Dans ce scénario, l’URI peut être /orders/99/customer. Toutefois, l’extension de ce modèle trop loin peut devenir fastidieuse à implémenter. Une meilleure approche consiste à inclure des liens dans le corps du message de réponse HTTP afin que les clients puissent facilement accéder aux ressources associées. Utiliser l’hypertexte comme moteur d’état d’application (HATEOAS) pour permettre la navigation vers les ressources associées décrit ce mécanisme plus en détail.

  • Gardez les relations simples et flexibles. Dans des systèmes plus complexes, vous pouvez être enclin à fournir des URI qui permettent au client de parcourir plusieurs niveaux de relations, tels que /customers/1/orders/99/products. Toutefois, ce niveau de complexité peut être difficile à maintenir et est inflexible si les relations entre les ressources changent à l’avenir. Au lieu de cela, essayez de conserver les URI relativement simples. Une fois qu’une application a une référence à une ressource, vous devez être en mesure d’utiliser cette référence pour rechercher des éléments liés à cette ressource. Vous pouvez remplacer la requête précédente par l’URI /customers/1/orders pour rechercher toutes les commandes du client 1, puis l’utiliser /orders/99/products pour rechercher les produits dans cet ordre.

    Conseil / Astuce

    Évitez d’exiger des URI de ressources plus complexes que collection/élément/collection.

  • Évitez un grand nombre de petites ressources. Toutes les requêtes web imposent une charge sur le serveur web. Plus les demandes sont importantes, plus la charge est importante. Les API web qui exposent un grand nombre de petites ressources sont appelées API web chatty. Essayez d’éviter ces API, car elles nécessitent qu’une application cliente envoie plusieurs requêtes pour rechercher toutes les données requises. Au lieu de cela, envisagez de dénormaliser les données et de combiner les informations associées en ressources plus volumineuses qui peuvent être récupérées via une seule requête. Toutefois, vous devez toujours équilibrer cette approche par rapport à la surcharge liée à l’extraction des données dont le client n’a pas besoin. La récupération d’objets volumineux peut augmenter la latence d’une requête et entraîner des coûts de bande passante supplémentaires. Pour plus d’informations sur ces antimodèles de performance, consultez E/S bavarde et Extraction superflue.

  • Évitez de créer des API qui reflètent la structure interne d’une base de données. L’objectif de REST est de modéliser les entités métier et les opérations qu’une application peut effectuer sur ces entités. Un client ne doit pas être exposé à l’implémentation interne. Par exemple, si vos données sont stockées dans une base de données relationnelle, l’API web n’a pas besoin d’exposer chaque table en tant que collection de ressources. Cette approche augmente la surface d’attaque et peut entraîner des fuites de données. Au lieu de cela, considérez l’API web comme une abstraction de la base de données. Si nécessaire, introduisez une couche de mappage entre la base de données et l’API web. Cette couche garantit que les applications clientes sont isolées des modifications apportées au schéma de base de données sous-jacent.

Conseil / Astuce

Il se pourrait qu'il ne soit pas possible de mapper chaque opération implémentée par une API web à une ressource spécifique. Vous pouvez gérer ces scénarios non-ressources via des requêtes HTTP qui appellent une fonction et retournent les résultats sous forme de message de réponse HTTP.

Par exemple, une API web qui implémente des opérations de calculatrice simples telles que l’ajout et la soustraction peuvent fournir des URI qui exposent ces opérations en tant que pseudo-ressources et utilisent la chaîne de requête pour spécifier les paramètres requis. Une requête GET à l’URI /add ?operand1=99&operand2=1 retourne un message de réponse avec le corps contenant la valeur 100.

Toutefois, vous devez utiliser ces formes d’URI avec parcimonie.

Définir les méthodes de l’API web RESTful

Les méthodes d’API web RESTful s’alignent sur les méthodes de requête et les types de supports définis par le protocole HTTP. Cette section décrit les méthodes de requête les plus courantes et les types de supports utilisés dans les API web RESTful.

Méthodes de requête HTTP

Le protocole HTTP définit de nombreuses méthodes de requête qui indiquent l’action que vous souhaitez effectuer sur une ressource. Les méthodes les plus courantes utilisées dans les API web RESTful sont GET, POST, PUT, PATCH et DELETE. Chaque méthode correspond à une opération spécifique. Lorsque vous concevez une API web RESTful, utilisez ces méthodes de manière cohérente avec la définition du protocole, la ressource accessible et l’action en cours d’exécution.

Il est important de se rappeler que l’effet d’une méthode de requête spécifique doit dépendre si la ressource est une collection ou un élément individuel. Le tableau suivant inclut certaines conventions que la plupart des implémentations RESTful utilisent.

Importante

Le tableau suivant utilise un exemple d’entité de commerce customer électronique. Une API web n’a pas besoin d’implémenter toutes les méthodes de requête. Les méthodes qu’il implémente dépendent du scénario spécifique.

Ressource PUBLIER OBTENIR METTRE SUPPRIMER
/clientèle Créer un client Récupérer tous les clients Mise à jour en bloc des clients Supprimer tous les clients
/customers/1 Erreur Récupérer les détails du client 1 Mettre à jour les détails du client 1 s’il existe Supprimer le client 1
/customers/1/orders Créer une commande pour le client 1 Récupérer toutes les commandes pour le client 1 Mise à jour en bloc des commandes pour le client 1 Supprimer toutes les commandes pour le client 1

Demandes d’GET

Une requête GET récupère une représentation de la ressource au niveau de l’URI spécifié. Le corps du message de réponse contient les détails de la ressource demandée.

Une requête GET doit retourner l’un des codes d’état HTTP suivants :

Code d’état HTTP Motif
200 (OK) La méthode a retourné la ressource avec succès.
204 (Pas de contenu) Le corps de la réponse ne contient aucun contenu, par exemple lorsqu’une requête de recherche ne retourne aucune correspondance dans la réponse HTTP.
404 (Introuvable) La ressource demandée est introuvable.

Demandes d’POST

Une requête POST doit créer une ressource. Le serveur affecte un URI pour la nouvelle ressource et retourne cet URI au client.

Importante

Pour les requêtes POST, un client ne doit pas tenter de créer son propre URI. Le client doit soumettre la demande à l’URI de la collection, et le serveur doit affecter un URI à la nouvelle ressource. Si un client tente de créer son propre URI et émet une requête POST vers un URI spécifique, le serveur retourne le code d’état HTTP 400 (REQUÊTE INCORRECTE) pour indiquer que la méthode n’est pas prise en charge.

Dans un modèle RESTful, les requêtes POST sont utilisées pour ajouter une nouvelle ressource à la collection que l’URI identifie. Toutefois, une requête POST peut également être utilisée pour envoyer des données à des fins de traitement à une ressource existante, sans la création d’une nouvelle ressource.

Une requête POST doit retourner l’un des codes d’état HTTP suivants :

Code d’état HTTP Motif
200 (OK) La méthode a effectué un traitement, mais ne crée pas de ressource. Le résultat de l’opération peut être inclus dans le corps de la réponse.
201 (créé) La ressource a été créée avec succès. L’URI de la nouvelle ressource est inclus dans l’en-tête Location de la réponse. Le corps de la réponse contient une représentation de la ressource.
204 (Pas de contenu) Le corps de la réponse ne contient aucun contenu.
400 (Requête incorrecte) Le client a placé des données non valides dans la requête. Le corps de la réponse peut contenir plus d’informations sur l’erreur ou un lien vers un URI qui fournit plus de détails.
405 (Méthode non autorisée) Le client a tenté d’effectuer une requête POST vers un URI qui ne prend pas en charge les requêtes POST.

Requête d’PUT

Une demande PUT doit mettre à jour une ressource existante s’il existe ou, dans certains cas, créer une ressource si elle n’existe pas. Pour effectuer une requête PUT :

  1. Le client spécifie l’URI de la ressource et inclut un corps de requête qui contient une représentation complète de la ressource.
  2. Le client effectue la requête.
  3. Si une ressource avec cet URI existe déjà, elle est remplacée. Sinon, une nouvelle ressource est créée si l’itinéraire le prend en charge.

Les méthodes PUT sont appliquées aux ressources qui sont des éléments individuels, tels qu’un client spécifique, au lieu de regroupements. Un serveur peut prendre en charge les mises à jour, mais pas la création via PUT. La prise en charge de la création via PUT dépend du fait que le client peut affecter de manière significative et fiable un URI à une ressource avant que celle-ci n'existe. Si ce n’est pas le cas, utilisez POST pour créer des ressources et que le serveur attribue l’URI. Utilisez ensuite PUT ou PATCH pour mettre à jour l’URI.

Importante

Les requêtes PUT doivent être idempotentes, ce qui signifie que l’envoi de la même requête plusieurs fois entraîne toujours la modification de la même ressource avec les mêmes valeurs. Si un client renvoie une demande PUT, les résultats doivent rester inchangés. En revanche, les requêtes POST et PATCH ne sont pas garanties d’être idempotentes.

Une requête PUT doit retourner l’un des codes d’état HTTP suivants :

Code d’état HTTP Motif
200 (OK) La ressource a été mise à jour avec succès.
201 (créé) La ressource a été créée avec succès. Le corps de la réponse peut contenir une représentation de la ressource.
204 (Pas de contenu) La ressource a été mise à jour avec succès, mais le corps de la réponse ne contient aucun contenu.
409 (Conflit) Impossible de terminer la requête en raison d’un conflit avec l’état actuel de la ressource.

Conseil / Astuce

Envisagez d’implémenter des opérations HTTP PUT en bloc qui peuvent effectuer des mises à jour par lots vers plusieurs ressources d’une collection. La requête PUT doit spécifier l’URI de la collection. Le corps de la demande doit spécifier les détails des ressources à modifier. Cette approche peut aider à réduire les chattines et à améliorer les performances.

requêtes PATCH

Une requête PATCH effectue une mise à jour partielle vers une ressource existante. Le client spécifie l’URI de la ressource. Le corps de la demande spécifie un ensemble de modifications à appliquer à la ressource. Cette méthode peut être plus efficace que l’utilisation de requêtes PUT, car le client envoie uniquement les modifications et non la représentation entière de la ressource. PATCH peut également créer une ressource en spécifiant un ensemble de mises à jour vers une ressource vide ou null si le serveur prend en charge cette action.

Avec une requête PATCH, le client envoie un ensemble de mises à jour à une ressource existante sous la forme d’un document patch. Le serveur traite le document de correctif pour effectuer la mise à jour. Le document patch spécifie uniquement un ensemble de modifications à appliquer au lieu de décrire l’intégralité de la ressource. La spécification de la méthode PATCH, RFC 5789, ne définit pas de format spécifique pour les documents patch. Le format doit être déduit du type de média dans la requête.

JSON est l’un des formats de données les plus courants pour les API web. Les deux principaux formats de correctifs basés sur JSON sont le correctif JSON et le correctif de fusion JSON.

Le patch de fusion JSON est plus simple que le patch JSON. Le document patch a la même structure que la ressource JSON d’origine, mais il inclut uniquement le sous-ensemble de champs qui doivent être modifiés ou ajoutés. En outre, un champ peut être supprimé en spécifiant null la valeur du champ dans le document patch. Cette spécification signifie que le correctif de fusion ne convient pas si la ressource d’origine peut avoir des valeurs null explicites.

Par exemple, supposons que la ressource d’origine a la représentation JSON suivante :

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

Voici un correctif de fusion JSON possible pour cette ressource :

{
    "price":12,
    "color":null,
    "size":"small"
}

Ce correctif de fusion indique au serveur de mettre à jour price, supprimer coloret ajouter size. Les valeurs pour name et category ne sont pas modifiées. Pour plus d’informations sur le correctif de fusion JSON, consultez RFC 7396. Le type de média pour le correctif de fusion JSON est application/merge-patch+json.

Un patch de fusion ne convient pas si la ressource d'origine peut contenir des valeurs nulles explicites en raison de la signification spéciale du document de correction null. Le document de correctif ne spécifie pas non plus l’ordre dans lequel le serveur doit appliquer les mises à jour. Que cet ordre soit important dépend des données et du domaine. Le correctif JSON, défini dans RFC 6902, est plus flexible, car il spécifie les modifications en tant que séquence d’opérations à appliquer, notamment ajouter, supprimer, remplacer, copier et tester pour valider les valeurs. Le type de média pour le correctif JSON est application/json-patch+json.

Une requête PATCH doit retourner l’un des codes d’état HTTP suivants :

Code d’état HTTP Motif
200 (OK) La ressource a été mise à jour avec succès.
400 (Requête incorrecte) Document de correctif incorrect.
409 (Conflit) Le document patch est valide, mais les modifications ne peuvent pas être appliquées à la ressource dans son état actuel.
415 (type de média non pris en charge) Le format de document de correctif n’est pas pris en charge.

Demandes de suppression

Une demande DELETE supprime la ressource à l’URI spécifié. Une requête DELETE doit retourner l’un des codes d’état HTTP suivants :

Code d’état HTTP Motif
204 (AUCUN CONTENU) La ressource a été supprimée avec succès. Le processus a été géré avec succès et le corps de la réponse ne contient aucune information supplémentaire.
404 (INTROUVABLE) La ressource n’existe pas.

Types MIME de ressource

La représentation des ressources est la façon dont une ressource identifiée par l’URI est encodée et transportée sur le protocole HTTP dans un format spécifique, tel que XML ou JSON. Les clients qui souhaitent récupérer une ressource spécifique doivent utiliser l’URI dans la requête à l’API. L’API répond en retournant une représentation de ressource des données indiquées par l’URI.

Dans le protocole HTTP, les formats de représentation des ressources sont spécifiés à l’aide de types multimédias, également appelés types MIME. Pour les données non liées, la plupart des API web prennent en charge JSON (type de média = application/json) et éventuellement XML (type de média = application/xml).

L’en-tête Content-Type dans une requête ou une réponse spécifie le format de représentation des ressources. L’exemple suivant illustre une requête POST qui inclut des données JSON :

POST https://api.contoso.com/orders
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Si le serveur ne prend pas en charge le type de média, il doit retourner le code d’état HTTP 415 (type de média non pris en charge).

Une demande cliente peut inclure un en-tête Accept qui contient une liste de types de supports que le client accepte du serveur dans le message de réponse. Par exemple:

GET https://api.contoso.com/orders/2
Accept: application/json, application/xml

Si le serveur ne peut correspondre à aucun des types multimédias répertoriés, il doit retourner le code d’état HTTP 406 (non acceptable).

Implémenter des méthodes asynchrones

Parfois, une méthode POST, PUT, PATCH ou DELETE peut nécessiter un traitement qui prend du temps. Si vous attendez la fin avant d’envoyer une réponse au client, cela peut entraîner une latence inacceptable. Dans ce scénario, envisagez de rendre la méthode asynchrone. Une méthode asynchrone doit retourner le code d’état HTTP 202 (accepté) pour indiquer que la demande a été acceptée pour traitement, mais est incomplète.

Exposez un point de terminaison qui retourne l’état d’une demande asynchrone afin que le client puisse surveiller l’état en interrogeant le point de terminaison d’état. Incluez l’URI du point de terminaison d’état dans l’en-tête Location de la réponse 202. Par exemple:

HTTP/1.1 202 Accepted
Location: /api/status/12345

Si le client envoie une requête GET à ce point de terminaison, la réponse doit contenir l’état actuel de la demande. Si vous le souhaitez, il peut inclure un délai estimé d’achèvement ou un lien pour annuler l’opération.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

Si l’opération asynchrone crée une ressource, le point de terminaison d’état doit retourner le code d’état 303 (Voir Autre) une fois l’opération terminée. Dans la réponse 303, incluez un en-tête Location qui donne l’URI de la nouvelle ressource :

HTTP/1.1 303 See Other
Location: /api/orders/12345

Pour plus d’informations, consultez Fournir une prise en charge asynchrone pour les requêtes longues et le modèle de Request-Reply asynchrone.

Implémenter la pagination et le filtrage des données

Pour optimiser la récupération des données et réduire la taille de charge utile, implémentez la pagination des données et le filtrage basé sur les requêtes dans votre conception d’API. Ces techniques permettent aux clients de demander uniquement le sous-ensemble de données dont ils ont besoin, ce qui peut améliorer les performances et réduire l’utilisation de la bande passante.

  • La pagination divise les jeux de données volumineux en blocs plus petits et gérables. Utilisez les paramètres de requête comme limit pour spécifier le nombre d’éléments à retourner et offset pour spécifier le point de départ. Veillez également à fournir des valeurs par défaut significatives pour limit et offset, telles que limit=25 et offset=0. Par exemple:

    GET /orders?limit=25&offset=50
    
    • limit: spécifie le nombre maximal d’éléments à retourner.

      Conseil / Astuce

      Pour éviter les attaques par déni de service, envisagez d’imposer une limite supérieure au nombre d’éléments retournés. Par exemple, si votre service définit max-limit=25et demande un client limit=1000, votre service peut retourner 25 éléments ou une erreur HTTP BAD-REQUEST, en fonction de la documentation de l’API.

    • offset: spécifie l’index de départ pour les données.

  • Le filtrage permet aux clients d’affiner le jeu de données en appliquant des conditions. L’API peut autoriser le client à passer le filtre dans la chaîne de requête de l’URI :

    GET /orders?minCost=100&status=shipped
    
    • minCost: filtre les commandes qui ont un coût minimal de 100.
    • status: filtre les commandes qui ont un état spécifique.

Envisagez les meilleures pratiques suivantes :

  • Le tri permet aux clients de trier les données à l’aide d’un sort paramètre comme sort=price.

    Importante

    L’approche de tri peut avoir un effet négatif sur la mise en cache, car les paramètres de chaîne de requête font partie de l’identificateur de ressource que de nombreuses implémentations de cache utilisent comme clé pour mettre en cache des données.

  • La sélection de champs pour les projections définies par le client permet aux clients de spécifier uniquement les champs dont ils ont besoin à l’aide d’un fields paramètre comme fields=id,name. Par exemple, vous pouvez utiliser un paramètre de chaîne de requête qui accepte une liste délimitée par des virgules de champs, tels que /orders ?fields=ProductID,Quantity.

Votre API doit valider les champs demandés pour vous assurer que le client est autorisé à y accéder et n’expose pas les champs qui ne sont normalement pas disponibles via l’API.

Prendre en charge les réponses partielles

Certaines ressources contiennent des champs binaires volumineux, tels que des fichiers ou des images. Pour surmonter les problèmes causés par des connexions non fiables et intermittentes et pour améliorer les temps de réponse, envisagez de prendre en charge la récupération partielle de ressources binaires volumineuses.

Pour prendre en charge les réponses partielles, l’API web doit prendre en charge l’en-tête Accept-Ranges pour les requêtes GET pour les ressources volumineuses. Cet en-tête indique que l’opération GET prend en charge les requêtes partielles. L’application cliente peut envoyer des requêtes GET qui retournent un sous-ensemble d’une ressource, spécifiée sous la forme d’une plage d’octets.

Envisagez également d’implémenter des requêtes HTTP HEAD pour ces ressources. Une requête HEAD est similaire à une requête GET, sauf qu’elle retourne uniquement les en-têtes HTTP qui décrivent la ressource, avec un corps de message vide. Une application cliente peut émettre une requête HEAD pour déterminer s’il faut extraire une ressource à l’aide de requêtes GET partielles. Par exemple:

HEAD https://api.contoso.com/products/10?fields=productImage

Voici un exemple de message de réponse :

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

L’en-tête Content-Length donne la taille totale de la ressource, et l’en-tête Accept-Ranges indique que l’opération GET correspondante prend en charge les résultats partiels. L’application cliente peut utiliser ces informations pour récupérer l’image en blocs plus petits. La première requête récupère les 2 500 premiers octets à l’aide de l’en-tête Range :

GET https://api.contoso.com/products/10?fields=productImage
Range: bytes=0-2499

Le message de réponse indique que cette réponse est partielle en retournant le code d’état HTTP 206. L’en-tête Content-Length spécifie le nombre réel d’octets retournés dans le corps du message et non la taille de la ressource. L’en-tête Content-Range indique quelle partie de la ressource est retournée (octets 0-2499 sur 4580) :

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

Une demande ultérieure de l’application cliente peut récupérer le reste de la ressource.

Implémenter HATEOAS

L’une des principales raisons d’utiliser REST est la possibilité de naviguer dans l’ensemble des ressources sans connaître préalablement le schéma d’URI. Chaque requête HTTP GET doit retourner les informations nécessaires pour rechercher les ressources liées directement à l’objet demandé via des liens hypertexte inclus dans la réponse. La demande doit également recevoir des informations qui décrivent les opérations disponibles sur chacune de ces ressources. Ce principe est appelé HATEOAS, ou hypertexte comme moteur d’état d’application. Le système est effectivement un ordinateur à état fini, et la réponse à chaque requête contient les informations nécessaires pour passer d’un état à un autre. Aucune autre information ne doit être nécessaire.

Remarque

Il n’existe pas de normes à usage général qui définissent comment modéliser le principe HATEOAS. Les exemples de cette section illustrent une solution propriétaire possible.

Par exemple, pour gérer la relation entre une commande et un client, la représentation d’une commande peut inclure des liens qui identifient les opérations disponibles pour le client de la commande. Le bloc de code suivant est une représentation possible :

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://api.contoso.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://api.contoso.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

Dans cet exemple, le links tableau a un ensemble de liens. Chaque lien représente une opération sur une entité associée. Les données de chaque lien incluent la relation (« client »), l’URI (https://api.contoso.com/customers/3), la méthode HTTP et les types MIME pris en charge. L’application cliente a besoin de ces informations pour appeler l’opération.

Le links tableau inclut également des informations de référencement automatique sur la ressource récupérée. Ces liens ont la relation auto.

L’ensemble de liens retournés peut changer en fonction de l’état de la ressource. L’idée que l’hypertexte est le moteur de l’état de l’application décrit ce scénario.

Implémenter le contrôle de version

Une API web ne reste pas statique. À mesure que les exigences métier changent, de nouvelles collections de ressources sont ajoutées. À mesure que de nouvelles ressources sont ajoutées, les relations entre les ressources peuvent changer et la structure des données dans les ressources peut être modifiée. La mise à jour d’une API web pour gérer les exigences nouvelles ou différentes est un processus simple, mais vous devez prendre en compte les effets que de telles modifications ont sur les applications clientes qui consomment l’API web. Le développeur qui conçoit et implémente une API web a un contrôle total sur cette API, mais il n’a pas le même degré de contrôle sur les applications clientes créées par les organisations partenaires. Il est important de continuer à prendre en charge les applications clientes existantes tout en permettant aux nouvelles applications clientes d’utiliser de nouvelles fonctionnalités et ressources.

Une API web qui implémente le contrôle de version peut indiquer les fonctionnalités et les ressources qu’elle expose, et une application cliente peut envoyer des demandes dirigées vers une version spécifique d’une fonctionnalité ou d’une ressource. Les sections suivantes décrivent plusieurs approches différentes, chacune ayant ses propres avantages et compromis.

Aucune gestion de version

Cette approche est la plus simple et peut fonctionner pour certaines API internes. Des modifications importantes peuvent être représentées sous forme de nouvelles ressources ou de nouveaux liens. L’ajout de contenu à des ressources existantes peut ne pas présenter de changement cassant, car les applications clientes qui ne s’attendent pas à voir ce contenu l’ignorent.

Par exemple, une demande adressée à l’URI https://api.contoso.com/customers/3 doit retourner les détails d’un seul client qui contient les champs id, name, et address attendus par l’application cliente :

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

Remarque

Par souci de simplicité, les exemples de réponses présentés dans cette section n’incluent pas de liens HATEOAS.

Si le DateCreated champ est ajouté au schéma de la ressource client, la réponse ressemble à ceci :

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

Les applications clientes existantes peuvent continuer à fonctionner correctement si elles peuvent ignorer les champs non reconnus. Entre-temps, de nouvelles applications clientes peuvent être conçues pour gérer ce nouveau champ. Toutefois, des modifications plus radicales apportées au schéma des ressources, y compris les suppressions de champs ou le changement de nom, peuvent se produire. Ou les relations entre les ressources peuvent changer. Ces mises à jour peuvent constituer des modifications importantes qui pourraient empêcher les applications clientes existantes de fonctionner correctement. Dans ces scénarios, envisagez l’une des approches suivantes :

Contrôle de version d’URI

Chaque fois que vous modifiez l’API web ou modifiez le schéma des ressources, vous ajoutez un numéro de version à l’URI pour chaque ressource. Les URI existants précédemment doivent continuer à fonctionner normalement en retournant des ressources conformes à leur schéma d’origine.

Par exemple, le address champ de l’exemple précédent est restructuré en sous-champs qui contiennent chaque partie constituante de l’adresse, telle que streetAddress, , citystate, et zipCode. Cette version de la ressource peut être exposée via un URI qui contient un numéro de version, tel que https://api.contoso.com/v2/customers/3:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Ce mécanisme de contrôle de version est simple, mais dépend du serveur pour acheminer la requête vers le point de terminaison approprié. Toutefois, il peut devenir difficile à mesure que l’API web mûrit à travers plusieurs itérations et que le serveur doit prendre en charge de nombreuses versions différentes. Du point de vue d’un puriste, dans tous les cas, les applications clientes récupèrent les mêmes données (client 3), de sorte que l’URI ne doit pas différer en fonction de la version. Ce schéma complique également l’implémentation de HATEOAS, car tous les liens doivent inclure le numéro de version dans leurs URI.

Contrôle de version de chaîne de requête

Au lieu de fournir plusieurs URI, vous pouvez spécifier la version de la ressource à l’aide d’un paramètre dans la chaîne de requête ajoutée à la requête HTTP, par https://api.contoso.com/customers/3?version=2exemple . Le paramètre de version doit être défini par défaut sur une valeur significative, comme 1, si les applications clientes plus anciennes l’omettent.

Cette approche présente l’avantage sémantique que la même ressource est toujours récupérée à partir du même URI. Toutefois, cette méthode dépend du code qui gère la requête pour analyser la chaîne de requête et renvoyer la réponse HTTP appropriée. Cette approche complique également l’implémentation de HATEOAS de la même façon que le mécanisme de contrôle de version d’URI.

Remarque

Certains navigateurs web et proxys web plus anciens ne utilisent pas de cache de réponses pour les requêtes qui incluent une chaîne de requête dans l’URI. Les réponses non mises en cache peuvent dégrader les performances des applications web qui utilisent une API web et s’exécuter à partir d’un navigateur web plus ancien.

Contrôle de version d’en-tête

Au lieu d’ajouter le numéro de version en tant que paramètre de chaîne de requête, vous pouvez implémenter un en-tête personnalisé qui indique la version de la ressource. Cette approche nécessite que l’application cliente ajoute l’en-tête approprié à toutes les demandes. Toutefois, le code qui gère la demande du client peut utiliser une valeur par défaut, comme la version 1, si l’en-tête de version est omis.

Les exemples suivants utilisent un en-tête personnalisé nommé Custom-Header. La valeur de cet en-tête indique la version de l’API web.

Version 1 :

GET https://api.contoso.com/customers/3
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

Version 2 :

GET https://api.contoso.com/customers/3
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","dateCreated":"2025-03-22T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Comme pour le contrôle de version d’URI et le contrôle de version de chaîne de requête, vous devez inclure l’en-tête personnalisé approprié dans tous les liens pour implémenter HATEOAS.

Contrôle de version du type de média

Lorsqu’une application cliente envoie une requête HTTP GET à un serveur web, elle doit utiliser un en-tête Accept pour spécifier le format du contenu qu’il peut gérer. En règle générale, l’objectif de l’en-tête Accept est de permettre à l’application cliente de spécifier si le corps de la réponse doit être XML, JSON ou un autre format commun que le client peut analyser. Toutefois, il est possible de définir des types de supports personnalisés qui incluent des informations qui permettent à l’application cliente d’indiquer la version d’une ressource attendue.

L’exemple suivant montre une requête qui spécifie un en-tête Accept avec la valeur application/vnd.contoso.v1+json. L’élément vnd.contoso.v1 indique au serveur web qu’il doit retourner la version 1 de la ressource. L’élément json spécifie que le format du corps de la réponse doit être JSON :

GET https://api.contoso.com/customers/3
Accept: application/vnd.contoso.v1+json

Le code qui gère la requête est chargé de traiter l’en-tête Accept et de l’honorer autant que possible. L’application cliente peut spécifier plusieurs formats dans l’en-tête Accept, ce qui permet au serveur web de choisir le format le plus approprié pour le corps de la réponse. Le serveur web confirme le format des données dans le corps de la réponse à l’aide de l’en-tête Content-Type :

HTTP/1.1 200 OK
Content-Type: application/vnd.contoso.v1+json; charset=utf-8

{"id":3,"name":"Fabrikam, Inc.","address":"1 Microsoft Way Redmond WA 98053"}

Si l’en-tête Accept ne spécifie aucun type de média connu, le serveur web peut générer un message de réponse HTTP 406 (non acceptable) ou retourner un message avec un type de média par défaut.

Ce mécanisme de contrôle de version est simple et bien adapté à HATEOAS, qui peut inclure le type MIME des données associées dans les liens de ressources.

Remarque

Lorsque vous sélectionnez une stratégie de contrôle de version, les implications, en particulier par rapport à la mise en cache des serveurs web. Le contrôle de version d’URI et les schémas de contrôle de version de chaîne de requête sont compatibles avec le cache, car la même combinaison d’URI ou de chaîne de requête fait référence aux mêmes données à chaque fois.

Les mécanismes de versionnage d’en-tête et de type de média nécessitent généralement davantage de logique pour examiner les valeurs dans l’en-tête personnalisé ou dans l’en-tête Accept. Dans un environnement à grande échelle, de nombreux clients utilisant différentes versions d’une API web peuvent entraîner une quantité importante de données dupliquées dans un cache côté serveur. Ce problème peut devenir aigu si une application cliente communique avec un serveur web via un proxy qui implémente la mise en cache et transfère uniquement une requête au serveur web si elle ne contient pas actuellement une copie des données demandées dans son cache.

API web multilocataire

Une solution d’API web mutualisée est partagée par plusieurs locataires, tels que des organisations distinctes qui ont leurs propres groupes d’utilisateurs.

Le multitenant affecte considérablement la conception des API web, car il détermine comment les ressources sont accessibles et identifiées par plusieurs locataires au sein d’une seule API web. Concevez une API en tenant compte du multitenant pour éviter la nécessité d'une refactorisation future visant à implémenter l'isolation, l'évolutivité, ou des personnalisations spécifiques aux clients.

Une API bien conçue doit clairement définir la façon dont les locataires sont identifiés dans les requêtes, que ce soit par le biais de sous-domaines, de chemins, d’en-têtes ou de jetons. Cette structure garantit une expérience cohérente et flexible pour tous les utilisateurs au sein du système. Pour plus d’informations, consultez Mapper les demandes aux locataires dans une solution mutualisée.

La multilocation affecte la structure des points de terminaison, la gestion des demandes, l’authentification et l’autorisation. Cette approche influence également la façon dont les passerelles d’API, les équilibreurs de charge et les services principaux routent et traitent les demandes. Les stratégies suivantes sont des façons courantes d’obtenir une architecture à multi-location dans une API web.

Utiliser une isolation par sous-domaine ou par domaine (tenance au niveau DNS)

Cette approche achemine les requêtes à l’aide de domaines spécifiques au locataire. Les domaines génériques utilisent des sous-domaines pour la flexibilité et la simplicité. Les domaines personnalisés, qui permettent aux locataires d’utiliser leurs propres domaines, offrent un meilleur contrôle et peuvent être adaptés pour répondre à des besoins spécifiques. Les deux méthodes s’appuient sur une configuration DNS appropriée, y compris A et CNAME des enregistrements, pour diriger le trafic vers l’infrastructure appropriée. Les domaines génériques simplifient la configuration, mais les domaines personnalisés offrent une expérience plus personnalisée.

Conservez le nom d’hôte entre le proxy inverse et les services principaux pour éviter des problèmes tels que la redirection d’URL et empêcher l’exposition d’URL internes. Cette méthode garantit le routage correct du trafic spécifique au locataire et contribue à protéger l’infrastructure interne. La résolution DNS est essentielle pour atteindre la résidence des données et garantir la conformité réglementaire.

GET https://adventureworks.api.contoso.com/orders/3

Transmettre des en-têtes HTTP spécifiques des locataires

Les informations de locataire peuvent être transmises via des en-têtes HTTP personnalisés, comme X-Tenant-ID ou X-Organization-ID, ou via des en-têtes basés sur l’hôte, comme Host ou X-Forwarded-Host, ou elles peuvent être extraites des revendications de JSON Web Token (JWT). Le choix dépend des fonctionnalités de routage de votre passerelle d’API ou proxy inverse, avec des solutions basées sur l’en-tête nécessitant une passerelle de couche 7 (L7) pour inspecter chaque requête. Cette exigence ajoute une surcharge de traitement, ce qui augmente les coûts de calcul lorsque le trafic est mis à l’échelle. Toutefois, l’isolation basée sur l’en-tête offre des avantages clés. Il permet l’authentification centralisée, ce qui simplifie la gestion de la sécurité entre les API mutualisées. En utilisant les SDK ou les clients API, le contexte de locataire est géré dynamiquement pendant l'exécution, ce qui réduit la complexité de la configuration côté-client. En outre, la conservation du contexte du locataire dans les en-têtes entraîne une conception d’API RESTful plus propre en évitant les données spécifiques au locataire dans l’URI.

Un facteur important pour le routage basé sur l’en-tête est qu’il complique la mise en cache, en particulier lorsque les couches de cache s’appuient uniquement sur des clés basées sur l’URI et ne comptent pas pour les en-têtes. Étant donné que la plupart des mécanismes de mise en cache optimisent les recherches d’URI, l’utilisation d’en-têtes peut entraîner des entrées de cache fragmentées. Les entrées fragmentées réduisent les accès au cache et augmentent la charge back-end. Plus critiquement, si une couche de mise en cache ne différencie pas les réponses par en-têtes, elle peut servir des données mises en cache destinées à un autre locataire et créer un risque de fuite de données.

GET https://api.contoso.com/orders/3
X-Tenant-ID: adventureworks

ou

GET https://api.contoso.com/orders/3
Host: adventureworks

ou

GET https://api.contoso.com/orders/3
Authorization: Bearer <JWT-token including a tenant-id: adventureworks claim>

Transmettre des informations spécifiques des locataires via le chemin URI

Cette approche ajoute des identificateurs de locataire dans la hiérarchie de ressources et s’appuie sur la passerelle d’API ou le proxy inverse pour déterminer le locataire approprié en fonction du segment de chemin d’accès. L’isolation basée sur le chemin est efficace, mais elle compromet la conception RESTful de l’API web et introduit une logique de routage plus complexe. Il nécessite souvent des critères correspondants ou des expressions régulières pour analyser et canoniquer le chemin d’URI.

En revanche, l’isolation basée sur l’en-tête transmet les informations de locataire via des en-têtes HTTP sous forme de paires clé-valeur. Ces deux approches permettent un partage d’infrastructure efficace pour réduire les coûts opérationnels et améliorer les performances dans les API web multilocataire à grande échelle.

GET https://api.contoso.com/tenants/adventureworks/orders/3

Activer le suivi distribué et le contexte de trace dans les API

À mesure que les systèmes distribués et les architectures de microservice deviennent la norme, la complexité des architectures modernes augmente. L’utilisation d’en-têtes, tels que Correlation-ID, X-Request-ID, ou X-Trace-ID, pour propager le contexte de trace dans les requêtes d’API est une bonne pratique pour obtenir une visibilité de bout en bout. Cette approche permet le suivi des requêtes au fur et à mesure qu’elles transitent du client vers les services principaux. Il facilite l’identification rapide des défaillances, surveille la latence et mappe les dépendances d’API entre les services.

Les API qui prennent en charge l’inclusion d’informations de trace et de contexte améliorent leur niveau d’observabilité et leurs fonctionnalités de débogage. En activant le suivi distribué, ces API permettent une compréhension plus précise du comportement du système et facilitent le suivi, le diagnostic et la résolution des problèmes dans les environnements complexes et multiservices.

GET https://api.contoso.com/orders/3
Correlation-ID: aaaa0000-bb11-2222-33cc-444444dddddd
HTTP/1.1 200 OK
...
Correlation-ID: aaaa0000-bb11-2222-33cc-444444dddddd

{...}

Modèle de maturité de l’API web

En 2008, Leonard Richardson a proposé ce qu’on appelle maintenant le modèle de maturité de Richardson (RMM) pour les API web. Le RMM définit quatre niveaux de maturité pour les API web et repose sur les principes de REST comme approche architecturale de la conception de services web. Dans le RMM, à mesure que le niveau de maturité augmente, l’API devient plus RESTful et suit de plus près les principes de REST.

Les niveaux sont les suivants :

  • Niveau 0 : Définissez un URI et toutes les opérations sont des requêtes POST adressées à cet URI. Les services web de protocole d’accès aux objets simples sont généralement à ce niveau.
  • Niveau 1 : Créez des URI distincts pour des ressources individuelles. Ce niveau n’est pas encore RESTful, mais il commence à s’aligner sur la conception RESTful.
  • Niveau 2 : Utilisez des méthodes HTTP pour définir des opérations sur les ressources. Dans la pratique, de nombreuses API web publiées s’alignent approximativement sur ce niveau.
  • Niveau 3 : Utiliser hypermedia (HATEOAS). Ce niveau est vraiment une API RESTful, en fonction de la définition de Fielding.

OpenAPI Initiative

L’Initiative OpenAPI a été créée par un consortium industriel pour normaliser les descriptions des API REST entre les fournisseurs. La spécification de normalisation a été appelée Swagger avant d’être introduite sous l’Initiative OpenAPI et renommée en Spécification OpenAPI (OAS).

Vous pouvez adopter OpenAPI pour vos API web RESTful. Tenez compte des points suivants :

  • L’OAS est fourni avec un ensemble de directives opinionées pour la conception de l’API REST. Les instructions sont avantageuses pour l’interopérabilité, mais vous obligent à vous assurer que votre conception est conforme aux spécifications.

  • OpenAPI promeut une approche axée sur le contrat au lieu d’une approche axée sur l’implémentation. Contract-first signifie que vous concevez d’abord le contrat d’API (l’interface), puis écrivez du code qui implémente le contrat.

  • Les outils tels que Swagger (OpenAPI) peuvent générer des bibliothèques clientes ou de la documentation à partir de contrats API. Pour obtenir un exemple, consultez ASP.NET documentation de l’API web principale avec Swagger/OpenAPI.

Étapes suivantes