Commencer avec le repos

Présentation REST

REST signifie REpresentational State Transfer et a été inventé par Roy Fielding dans sa thèse de doctorat [Architectural Styles and the Design of Network-based Software Architectures][1 ]. Il y identifie des principes architecturaux spécifiques tels que :

  • Ressources adressables : l’abstraction clé des informations et des données dans REST est une ressource et chaque ressource doit être adressable via un URI.

  • Une interface uniforme et contrainte : utilisation d’un petit ensemble de méthodes bien définies pour manipuler nos ressources.

  • Orienté représentation : une ressource référencée par un URI peut avoir différents formats et différentes plates-formes ont besoin de différents formats, par exemple les navigateurs ont besoin de HTML, JavaScript a besoin de JSON et les applications Java peuvent avoir besoin de XML, JSON, CSV, texte, etc. Nous interagissons donc avec services utilisant la représentation de ce service.

  • Communiquez sans état : les applications sans état sont plus faciles à mettre à l’échelle.

  • L’hypermédia comme moteur de l’état des applications : laissez nos formats de données piloter les transitions d’état dans nos applications.

L’ensemble de ces principes architecturaux est appelé REST. Les concepts de REST sont inspirés de celui de HTTP. Roy Fielding qui nous a donné REST est également l’un des auteurs des spécifications HTTP.

[Web Services] [2] et RESTful Web Services sont des services qui sont exposés à Internet pour un accès programmatique. Ce sont des API en ligne que nous pouvons appeler depuis notre code. Il existe deux types de [« gros » services Web][3] de services Web SOAP et REST.

[Services Web RESTful][4] : les services Web qui sont écrits en appliquant les concepts architecturaux REST sont appelés services Web RESTful, qui se concentrent sur les ressources système et sur la façon dont l’état d’une ressource peut être transféré via le protocole HTTP à différents clients.

Ce document est uniquement axé sur les services Web RESTful, nous n’entrerons donc pas dans les détails de SOAP WS.

Il n’y a pas de règles strictes lors de la conception de services Web RESTful comme

  • Aucune norme de protocole
  • Aucune norme de canal de communication
  • Aucune norme de définition de service

Mais SOAP a des règles strictes pour tout cela. Tous les services Web SOAP suivent la spécification SOAP qui dicte ce que devraient être tous les services Web SOAP. Cette spécification est développée et gérée par un comité et si SOAP WS ne suit même pas une seule règle, alors par définition ce n’est pas SOAP.

Concepts de services Web RESTful

Il existe quelques directives à prendre en compte lors de la conception/du développement d’une API RESTful :

  1. Emplacements/URI basés sur les ressources
  2. Utilisation correcte des méthodes HTTP
  3. HATEOAS (Hypermedia comme moteur d’état d’application)

L’approche principale lors du développement d’API RESTful devrait être de rendre l’API “aussi RESTful que possible”.

[1] : http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm [2] : https://docs.oracle.com/javaee/6/tutorial/doc/gijvh.html [3] : https://docs.oracle.com/javaee/6/tutorial/doc/giqsx.html [4] : https://docs.oracle.com/javaee/6/tutorial/doc/giqsx.html#gkcaw

REST sur HTTP

REST est une architecture indépendante du protocole proposée par Roy Fielding dans sa thèse (le chapitre 5 étant la présentation de REST), qui généralise le concept éprouvé des navigateurs Web en tant que clients afin pour dissocier les clients d’un système distribué des serveurs.

Pour qu’un service ou une API soit RESTful, il doit respecter des contraintes données telles que :

  • Serveur client
  • Apatride
  • Cacheable
  • Système en couches
  • Interface uniforme
    • Resources identification
    • Resources representation
    • Self-descriptive messages
    • Hypermedia

Outre les contraintes mentionnées dans la thèse de Fielding, dans son article de blog REST APIs must be hypertext-driven , Fielding a précisé que ** le simple fait d’invoquer un service via HTTP ne le rend pas RESTful **. Un service doit donc également respecter d’autres règles qui sont résumées comme suit :

  • L’API doit respecter et ne pas violer le protocole sous-jacent. Bien que REST soit utilisé via HTTP la plupart du temps, il n’est pas limité à ce protocole.

  • Fort accent sur les ressources et leur présentation via les types de médias.

  • Les clients ne doivent pas avoir de connaissances initiales ou d’hypothèses sur les ressources disponibles ou leur état renvoyé ([ressource “typée”] (http://soabits.blogspot.co.at/2012/04/restful-resources-are-not-typed .html)) dans une API mais apprenez-les à la volée via des requêtes émises et des réponses analysées. Cela donne au serveur la possibilité de se déplacer ou de renommer facilement des ressources sans interrompre une implémentation client.

Modèle de maturité de Richardson

Le Richardson Maturity Model est un moyen d’appliquer des contraintes REST sur HTTP afin d’obtenir des services Web RESTful.

Leonard Richardson a divisé les applications en ces 4 couches :

  • Niveau 0 : utilisation de HTTP pour le transport
  • Niveau 1 : utilisation d’URL pour identifier les ressources
  • Niveau 2 : utilisation des verbes HTTP et des statuts pour les interactions
  • Niveau 3 : utilisation de HATEOAS

Comme l’accent est mis sur la représentation de l’état d’une ressource, la prise en charge de plusieurs représentations pour la même ressource est encouragée. Une représentation pourrait donc présenter une vue d’ensemble de l’état de la ressource tandis qu’une autre renvoie les détails complets de la même ressource.

Notez également que compte tenu des contraintes Fielding, une API n’est effectivement RESTful qu’une fois le 3e niveau du RMM implémenté.


Requêtes et réponses HTTP

Une requête HTTP est :

  • Un verbe (ou méthode), la plupart du temps parmi GET, [POST](https://tools.ietf .org/html/rfc7231#section-4.3.3), METTRE, [SUPPRIMER](https://tools.ietf .org/html/rfc7231#section-4.3.5) ou PATCH
  • Une URL
  • En-têtes (paires clé-valeur)
  • Eventuellement un corps (c’est-à-dire charge utile, données)

Une réponse HTTP est :

  • Un statut, la plupart du temps parmi 2xx (succès), [4xx (erreur client)](https://tools. ietf.org/html/rfc7231#section-6.5) ou 5xx (erreur de serveur)
  • En-têtes (paires clé-valeur)
  • Un corps (charge utile, données)

Caractéristiques des verbes HTTP :

  • Verbes qui ont un corps : POST, PUT, PATCH
  • Verbes qui doivent être sûrs (c’est-à-dire qui ne doivent pas modifier les ressources) : GET
  • Verbes qui doivent être idempotents (c’est-à-dire qui ne doivent pas affecter à nouveau les ressources lorsqu’ils sont exécutés plusieurs fois) : GET (nullipotent), PUT, DELETE
        body  safe  idempotent
GET      ✗     ✔     ✔
POST     ✔     ✗     ✗
PUT      ✔     ✗     ✔
DELETE   ✗     ✗     ✔
PATCH    ✔     ✗     ✗

Par conséquent, les verbes HTTP peuvent être comparés aux fonctions CRUD :

  • [Create](https://www.wikiod.com/fr/http/http-pour-les-api#Créer une ressource) : ‘POST’
  • READ : OBTENIR
  • [UPDATE](https://www.wikiod.com/fr/http/http-pour-les-api#Modifier une ressource): PUT, PATCH
  • [DSUPPRIMER](https://www.wikiod.com/fr/http/http-pour-les-api#Supprimer une ressource): SUPPRIMER

Notez que une requête PUT demande aux clients d’envoyer la totalité de la ressource avec les valeurs mises à jour. Pour mettre à jour partiellement une ressource, un verbe PATCH peut être utilisé (voir Comment mettre à jour partiellement une ressource ?).

Statuts de réponse HTTP habituels

Succès

Redirection

  • 304 (NOT MODIFIED) : le client peut utiliser la version en cache dont il dispose de la ressource demandée

Erreurs client

  • 401 (UNAUTHORIZED) : une requête anonyme accède à une API protégée
  • 403 (FORBIDDEN) : une requête authentifiée n’a pas assez de droits pour accéder à une API protégée
  • 404 (NOT FOUND) : ressource introuvable
  • 409 (CONFLIT) : état de la ressource en conflit (par exemple, un utilisateur essayant de créer un compte avec une adresse e-mail déjà enregistrée)
  • 410 (GONE) : identique à 404, mais la ressource existait
  • 412 (PRECONDITION FAILED) : la requête tente de modifier une ressource qui se trouve dans un état inattendu
  • 422 (UNPROCESSABLE ENTITY) : la charge utile de la requête est syntaxiquement valide, mais sémantiquement erronée (par exemple, un champ obligatoire qui n’a pas été valorisé)
  • 423 (LOCKED) : la ressource est verrouillée
  • 424 (FAILED DEPENDENCY) : l’action demandée dépendait d’une autre action qui a échoué
  • 429 (TOO MANY REQUESTS) : l’utilisateur a envoyé trop de requêtes dans un laps de temps donné
  • Sinon : 400 (BAD REQUEST)

Erreurs de serveur

Remarques

Rien ne vous empêche d’ajouter un corps aux réponses erronées, pour rendre le rejet plus clair pour les clients. Par exemple, 422 (UNPROCESSABLE ENTITY) est un peu vague : le corps de la réponse doit fournir la raison pour laquelle l’entité n’a pas pu être traitée.


HATEOAS

Chaque ressource doit fournir un hypermédia aux ressources auxquelles elle est liée. Un lien est au moins composé par :

  • Un rel (pour relation, alias nom) : décrit la relation entre la ressource principale et la ou les ressources liées
  • Un href : l’URL ciblant la ou les ressources liées

Des attributs supplémentaires peuvent également être utilisés pour faciliter la dépréciation, la négociation de contenu, etc.

Cormac Mulhall explique que le client doit décider quel verbe HTTP utiliser en fonction de ce qu’il essaie de faire. En cas de doute, la documentation de l’API devrait de toute façon vous aider à comprendre les interactions disponibles avec tous les hypermédias.


Types de médias

Les types de médias aident à avoir des messages auto-descriptifs. Ils jouent le rôle de contrat entre clients et serveurs, afin qu’ils puissent échanger des ressources et des hypermédias.

Bien que application/json et application/xml soient des types de média assez populaires, ils ne contiennent pas beaucoup de sémantique. Ils décrivent simplement la syntaxe globale utilisée dans le document. Des types de médias plus spécialisés qui prennent en charge les exigences HATEOAS doivent être utilisés (ou étendus via vendor media types), tels que :

Un client indique à un serveur quels types de médias il comprend en ajoutant l’en-tête “Accepter” à sa requête, par exemple :

Accept: application/hal+json

Si le serveur n’est pas en mesure de produire la ressource demandée dans une telle représentation, il renvoie un 406 (NOT ACCEPTABLE). Sinon, il ajoute le type de média dans l’en-tête Content-Type de la réponse contenant la ressource représentée, par exemple :

Content-Type: application/hal+json

Sans état > avec état

Pourquoi?

Un serveur avec état implique que les sessions des clients sont stockées dans un stockage local d’instance de serveur (presque toujours dans des sessions de serveur Web). Cela commence à être un problème lorsque vous essayez de [mettre à l’échelle horizontalement] (http://stackoverflow.com/a/11715598/1225328) : si vous cachez plusieurs instances de serveur derrière un équilibreur de charge, si un client est d’abord envoyé à instance # 1 lors de la connexion, mais ensuite à instance #2 lors de la récupération d’une ressource protégée par exemple, la deuxième instance traitera la demande de manière anonyme, car **la session client a été stockée localement dans *instance #1 ***.

Des solutions ont été trouvées pour résoudre ce problème (par exemple en configurant session replication and/or sticky session), mais l’architecture REST propose une autre approche : il suffit de ne pas rendez votre serveur avec état, * rendez-le sans état *. Selon Fielding:

Chaque requête du client au serveur doit contenir toutes les informations nécessaires à la compréhension de la requête, et ne peut profiter d’aucun contexte stocké sur le serveur. L’état de la session est donc entièrement conservé sur le client.

En d’autres termes, une requête doit être traitée exactement de la même manière, qu’elle soit envoyée à l’instance #1 ou à l’instance #2. C’est pourquoi les applications sans état sont considérées comme * plus faciles à mettre à l’échelle *.

Comment?

Une approche courante est une [authentification basée sur des jetons] (http://stackoverflow.com/a/35316102/1225328), en particulier avec les [jetons Web JSON] à la mode (https://tools.ietf.org/html/rfc7519 ). Notez que JWT a encore quelques problèmes, en particulier concernant l’invalidation et la prolongation automatique de l’expiration (c’est-à-dire la fonction se souvenir de moi).

Notes d’accompagnement

L’utilisation de cookies ou d’en-têtes (ou quoi que ce soit d’autre) n’a rien à voir avec le fait que le serveur soit avec état ou sans état : ce ne sont que des médias qui sont ici utilisés pour transporter des jetons (identifiant de session pour les serveurs avec état, JWT, etc.), rien de plus.

Lorsqu’une API RESTful est uniquement utilisée par les navigateurs, (HttpOnly et [secure](https://en.wikipedia.org/wiki/HTTP_cookie# Les cookies Secure_cookie)) peuvent être très pratiques car les navigateurs les joignent automatiquement aux demandes sortantes. Il convient de mentionner que si vous optez pour les cookies, soyez conscient de CSRF (une bonne façon de l’empêcher est de faire en sorte que les clients générer et envoyer la même valeur secrète unique dans un cookie et un en-tête HTTP personnalisé ).


API pouvant être mise en cache avec [demandes conditionnelles] (https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests)

Avec l’en-tête Last-Modified

Le serveur peut fournir un en-tête de date “Last-Modified” aux réponses contenant des ressources pouvant être mises en cache. Les clients doivent ensuite stocker cette date avec la ressource.

Désormais, chaque fois que les clients demandent à l’API de lire la ressource, ils peuvent ajouter à leurs requêtes un en-tête “If-Modified-Since” contenant la dernière date de « dernière modification » qu’ils ont reçue et stockée. Le serveur doit alors comparer l’en-tête de la requête et la date réelle de dernière modification de la ressource. S’ils sont égaux, le serveur renvoie un 304 (NOT MODIFIED) avec un corps vide : le client demandeur doit utiliser la ressource actuellement mise en cache. a.

De plus, lorsque les clients demandent à l’API de mettre à jour la ressource (c’est-à-dire avec un verbe non sécurisé), ils peuvent ajouter un [en-tête “If-Unmodified-Since”] (https://tools.ietf.org/html/rfc7232#section- 3.4). Cela aide à gérer les conditions de concurrence : si l’en-tête et la date de dernière modification réelle sont différents, le serveur renvoie un 412 (PRÉCONDITION FAILED). Le client doit alors lire le nouvel état de la ressource avant de réessayer de modifier la ressource.

Avec l’en-tête ETag

Un ETag (balise d’entité) est un identifiant pour un état spécifique d’une ressource. Il peut s’agir d’un hachage MD5 de la ressource pour une validation forte, ou d’un identifiant spécifique au domaine pour un [ validation faible] (https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests#Weak_validation).

Fondamentalement, le processus est le même qu’avec l’en-tête Last-Modified : le serveur fournit un en-tête ETag aux réponses contenant ressources qui peuvent être mises en cache, et les clients doivent alors stocker cet identifiant avec la ressource.

Ensuite, les clients fournissent un If-None-Match header lorsqu’ils veulent lire la ressource, contenant le dernier ETag qu’ils ont reçu et stocké . Le serveur peut désormais renvoyer un 304 (NOT MODIFIED) si l’en-tête correspond à l’ETag réel de la ressource.

Là encore, les clients peuvent fournir un If-Match header lorsqu’ils souhaitent modifier la ressource, et le serveur doit renvoyer un 412 (PRÉCONDITION ÉCHEC) si l’ETag fourni ne correspond pas à l’ETag réel.

Notes complémentaires

ETag > date

Si les clients fournissent à la fois la date et l’ETag dans leurs demandes, la date doit être ignorée. De RFC 7232 (ici et ici) :

Un destinataire DOIT ignorer If-Modified-Since/If-Unmodified-Since si la requête contient un champ d’en-tête If-None-Match/If-Match ; la condition dans If-None-Match/If-Match est considérée comme un remplacement plus précis de la condition dans If-Modified-Since/If-Unmodified-Since, et les deux ne sont que combinées dans le but d’interopérer avec des intermédiaires plus anciens qui pourraient ne pas implémenter If-None-Match/If-Match.

ETags peu profonds

De plus, bien qu’il soit assez évident que les dates de dernière modification sont conservées avec les ressources côté serveur, plusieurs approches sont disponibles avec ETag.

Une approche habituelle consiste à implémenter des ETags peu profonds : le serveur traite la requête comme si aucun en-tête conditionnel n’avait été donné, mais à la toute fin seulement, il génère l’ETag de la réponse qu’il est sur le point de renvoyer (par exemple en le hachant), et compare avec celui fourni. Ceci est relativement facile à mettre en œuvre car seul un intercepteur HTTP est nécessaire (et de nombreuses implémentations existent déjà en fonction du serveur). Cela étant dit, il convient de mentionner que cette approche économisera de la bande passante mais pas des performances du serveur :

Une implémentation plus approfondie du mécanisme ETag pourrait potentiellement offrir des avantages bien plus importants - comme servir certaines requêtes à partir du cache et ne pas avoir à effectuer le calcul du tout - mais l’implémentation ne serait certainement pas aussi simple, ni aussi enfichable comme l’approche peu profonde décrite ici.


Les pièges courants

Pourquoi ne devrais-je pas mettre de verbes dans une URL ?

HTTP n’est pas RPC : ce qui rend HTTP significativement différent de RPC, c’est que les requêtes sont dirigées vers Ressources. Après tout, URL signifie Uniform Resource Locator, et une URL est un URI : un Uniform Resource Idenfitier. L’URL cible la ressource que vous voulez traiter, la méthode HTTP indique ce que vous voulez en faire. Les méthodes HTTP sont aussi appelées verbes : les verbes dans les URL n’ont alors aucun sens. Notez que les relations HATEOAS ne doivent pas non plus contenir de verbes, car les liens ciblent également des ressources.

Comment mettre à jour partiellement une ressource ?

Comme les requêtes PUT demandent aux clients d’envoyer la ressource entière avec les valeurs mises à jour, PUT /users/123 ne peut pas être utilisé pour simplement mettre à jour l’email d’un utilisateur par exemple. Comme l’explique William Durand dans S’il vous plaît. Ne patchez pas comme un idiot., plusieurs solutions compatibles REST sont disponibles :

  • Exposez les propriétés de la ressource et utilisez la méthode PUT pour envoyer une valeur mise à jour, car la spécification PUT indique que * des mises à jour partielles du contenu sont possibles en ciblant une ressource identifiée séparément avec un état qui chevauche une partie de la plus grande ressource* :
PUT https://example.com/api/v1.2/users/123/email
body:
  [email protected]
  • Utilisez une requête PATCH contenant un ensemble d’instructions décrivant comment la ressource doit être modifiée (par exemple, en suivant JSON Patch) :
PATCH https://example.com/api/v1.2/users/123
body:
  [
    { "op": "replace", "path": "/email", "value": "[email protected]" }
  ]
  • Utiliser une requête PATCH contenant une représentation partielle de la ressource, comme proposé dans [Commentaire de Matt Chapman](http://williamdurand.fr/2014/02/14/Please-do-not-patch-like-an- idiot/#commentaire-1495702936):
PATCH https://example.com/api/v1.2/users/123
body:
  {
    "email": "[email protected]"
  }

Qu’en est-il des actions qui ne rentrent pas dans le monde des opérations CRUD ?

Citant Vinay Sahni dans Best Practices for Designing a Pragmatic RESTful API :

C’est là que les choses peuvent devenir floues. Il existe plusieurs approches :

  1. Restructurer l’action pour qu’elle apparaisse comme un champ d’une ressource. Cela fonctionne si l’action ne prend pas de paramètres. Par exemple, une action activate pourrait être mappée à un champ booléen « activé » et mise à jour via un PATCH vers la ressource.

  2. Traitez-la comme une sous-ressource avec les principes RESTful. Par exemple, l’API de GitHub vous permet de star a gist avec PUT /gists/:id/star et unstar avec DELETE /gists/:id/star.

  3. Parfois, vous n’avez vraiment aucun moyen de mapper l’action sur une structure RESTful sensible. Par exemple, une recherche multi-ressources n’a pas vraiment de sens pour être appliquée au point de terminaison d’une ressource spécifique. Dans ce cas, /search serait le plus logique même s’il ne s’agit pas d’une ressource. C’est OK - faites simplement ce qui est juste du point de vue du consommateur d’API et assurez-vous que c’est clairement documenté pour éviter toute confusion.


Pratiques courantes

  • L’API est documentée. Des outils sont disponibles pour vous aider à construire votre documentation, par ex. Swagger ou Spring REST Docs.

  • L’API est versionnée, soit via les en-têtes, soit via l’URL :

https://example.com/api/v1.2/blogs/123/articles
                        ^^^^
  • Les ressources ont [noms au pluriel] (http://stackoverflow.com/a/21809963/1225328) :
https://example.com/api/v1.2/blogs/123/articles
                             ^^^^^     ^^^^^^^^
  • Les URL utilisent kebab-case (les mots sont en minuscules et séparés par des tirets) :
https://example.com/api/v1.2/quotation-requests
                             ^^^^^^^^^^^^^^^^^^
  • HATEOAS fournit un lien “self” vers les ressources, ciblant elles-mêmes :
{
  ...,
  _links: {
    ...,
    self: { href: "https://example.com/api/v1.2/blogs/123/articles/789" }
    ^^^^
  }
}
  • Les relations HATEOAS utilisent lowerCamelCase (les mots sont en minuscules, puis en majuscules sauf le premier, et les espaces sont omis), pour permettre aux clients JavaScript d’utiliser la [notation par points](https://developer.mozilla.org/en-US/docs /Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation) tout en respectant les conventions de nommage JavaScript lors de l’accès aux liens :
{
  ...,
  _links: {
    ...,
    firstPage: { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=1&pageSize=25" }
    ^^^^^^^^^
  }
}

Gestion des blogs via une API HTTP RESTful

Les exemples suivants utilisent HAL pour exprimer HATEOAS et utilisent :

  • CURIE (URI compact) : utilisé pour fournir des liens vers la documentation de l’API
  • Modèles d’URI : URI qui inclut des paramètres qui doivent être remplacés avant que l’URI ne soit résolu

Obtenir le blog 123

Demande

GET https://example.com/api/v1.2/blogs/123
headers:
  Accept: application/hal+json

Réponse

status: 200 (OK)
headers:
  Content-Type: application/hal+json
body:
  {
    "id": 123,
    "title": "The blog title",
    "description": "The blog description",
    "_links": {
      "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
      "self": { "href": "https://example.com/api/v1.2/blogs/123" },
      "doc:articles": { "href": "https://example.com/api/v1.2/blogs/123/articles{?pageIndex,pageSize}", "templated": true }
    }
  }

Créer un nouvel article dans le blog 123

Demande

POST https://example.com/api/v1.2/blogs/123/articles
headers:
  Content-Type: application/json
  Accept: application/hal+json
  X-Access-Token: XYZ
body:
  {
    "title": "The title 2",
    "content": "The content 2"
  }

Réponse

status: 201 (CREATED)
headers:
  Content-Type: application/hal+json
body:
  {
    "id": 789,
    "title": "The title 2",
    "content": "The content 2",
    "_links": {
      "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
      "self": { "href": "https://example.com/api/v1.2/blogs/123/articles/789" },
      "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" },
      "doc:comments": { "href": "https://example.com/api/v1.2/blogs/123/articles/789/comments{?pageIndex,pageSize}", "templated": true }
    }
  }

Obtenir l’article 789 du blog 123

Demande

GET https://example.com/api/v1.2/blogs/123/articles/789
headers:
  Accept: application/hal+json

Réponse

status: 200 (OK)
headers:
  Content-Type: application/hal+json
body:
  {
    "id": 789,
    "title": "The title 2",
    "content": "The content 2",
    "_links": {
      "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
      "self": { "href": "https://example.com/api/v1.2/blogs/123/articles/789" },
      "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" },
      "doc:comments": { "href": "https://example.com/api/v1.2/blogs/123/articles/789/comments{?pageIndex,pageSize}", "templated": true }
    }
  }

Obtenez la 4ème page des 25 articles du blog 123

Demande

GET https://example.com/api/v1.2/blogs/123/articles?pageIndex=4&pageSize=25
headers:
  Accept: application/hal+json

Réponse

status: 200 (OK)
headers:
  Content-Type: application/hal+json
body:
  {
    "pageIndex": 4,
    "pageSize": 25,
    "totalPages": 26,
    "totalArticles": 648,
    "_link": {
      "firstPage": { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=1&pageSize=25" },
      "previousPage": { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=3&pageSize=25" },
      "self": { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=4&pageSize=25" },
      "nextPage": { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=5&pageSize=25" },
      "lastPage": { "href": "https://example.com/api/v1.2/blogs/123/articles?pageIndex=26&pageSize=25" }
    },
    "_embedded": [
      {
        ...
      }, {
        "id": 456,
        "title": "The title 1",
        "content": "The content 1",
        "_links": {
          "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
          "self": { "href": "https://example.com/api/v1.2/blogs/123/articles/456" },
          "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" },
          "doc:comments": { "href": "https://example.com/api/v1.2/blogs/123/articles/456/comments{?pageIndex,pageSize}", "templated": true }
        }
      }, {
        "id": 789,
        "title": "The title 2",
        "content": "The content 2",
        "_links": {
          "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
          "self": { "href": "https://example.com/api/v1.2/blogs/123/articles/789" },
          "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" },
          "doc:comments": { "href": "https://example.com/api/v1.2/blogs/123/articles/789/comments{?pageIndex,pageSize}", "templated": true }
        }
      }, {
        ...
      }
    ]
  }

#Mettre à jour l’article 789 du blog 123#

Demande

PUT https://example.com/api/v1.2/blogs/123/articles/789
headers:
  Content-Type: application/json
  Accept: application/hal+json
  X-Access-Token: XYZ
body:
  {
    "id": 789,
    "title": "The title 2 updated",
    "content": "The content 2 updated"
  }

Réponse

status: 200 (OK)
headers:
  Content-Type: application/hal+json
body:
  {
    "id": 789,
    "title": "The title 2 updated",
    "content": "The content 2 updated",
    "_links": {
      "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
      "self": { "href": "https://example.com/api/v1.2/blogs/123/articles/789" },
      "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" },
      "doc:comments": { "href": "https://example.com/api/v1.2/blogs/123/articles/789/comments{?pageIndex,pageSize}", "templated": true }
    }
  }

Remarques

  • L’identifiant qui sert à identifier la ressource à mettre à jour est celui de l’URL : celui du corps (le cas échéant) doit être silencieusement ignoré.
  • Comme une requête PUT met à jour l’ensemble de la ressource, si aucun contenu n’aurait été envoyé, il aurait dû être supprimé de la ressource persistante.

Supprimer l’article 789 du blog 123

Demande

DELETE https://example.com/api/v1.2/blogs/123/articles/789
headers:
  Accept: application/hal+json
  X-Access-Token: XYZ

Réponse

status: 204 (NO CONTENT)
headers:
  Content-Type: application/hal+json
body:
  {
    "_links": {
      "curies": [{ "name": "doc", "href": "https://example.com/docs/{rel}", "templated": true }],
      "doc:blog": { "href": "https://example.com/api/v1.2/blogs/123", "title": "The blog title" }
    }
  }

Violation de REST

<stock>
    <add>
        <item>
            <name>Milk</name>
            <quantity>2</quantity>
        </item>
    </add>
</stock>

Mettre ce corps dans une ressource comme /stocks/123 viole l’idée derrière REST. Bien que ce corps soit “mis” et qu’il contienne toutes les informations nécessaires, il est également accompagné d’un appel de méthode pour “ajouter” quelque part lorsque le corps est traité. Après REST, on publierait l’élément dans /stocks/123/items/.