REST avec XMLHttpRequest
19 mai 2011
Ces derniers temps j'ai du développer un service REST et ensuite, dialoguer avec par divers moyens. J'en suis venu à me poser la question des possibilités et limites d'usage d'un tel service avec JavaScript.
Cette mini étude a pour but de déterminer si nous pouvons utiliser un service collant le plus possible aux principes REST avec Javascript et XMLHttpRequest. Il n'est donc pas question de définir ce qui fait ou non un service REST. Seuls deux points particuliers nous intéressent :
- L'utilisation des 4 verbes les plus courants (GET, POST, PUT, DELETE)
- La gestion correcte des statuts des réponses HTTP
Pour réaliser les tests, j'ai écrit une petite application qui retourne les en-têtes HTTP reçus ainsi que les informations passées dans le corps ou en query string. C'est une sorte de echo amélioré. La partie cliente se contentait, elle, de retourner les en-têtes et résultats reçus.
Tous les tests ont été effectués sur le même site. Il n'y a pas eu d'accès à un autre hôte, ces problématiques étant encore très mal gérées par les navigateurs (même par ceux qui clament pouvoir le faire).
Enfin, ces tests ont été réalisés avec jQuery qui a fait un bon travail pour harmoniser les résultats. Si vous utilisez une autre librairie, ou pas de librairie du tout, vous pouvez avoir des résultats différents. Par exemple Internet Explorer comprend une réponse 204 comme 1223.
Généralités
Contrairement à pas mal de légendes circulant ici et là, vous pouvez parfaitement utiliser les 4 méthodes les plus courantes avec XMLHttpRequest (GET, POST, PUT, DELETE).
Les tests démontrent que toutes les réponses de type 200 sont très bien gérées par tous les navigateurs et sans surprise.
Les réponses en erreur (type 400 ou 500) passent sans problème sauf avec Opera si la réponse n'a pas de contenu. Il est donc nécessaire de toujours envoyer un contenu à ce type de réponse. Le problème ne se pose pas avec d'autres type de réponse (204 par exemple).
Les réponses de type 300 (redirection) sont les plus problématiques. Par défaut, le navigateur va suivre la redirection et ce comportement ne peut pas être modifié. Plus gênant, vous ne savez pas où vous êtes partis. La solution peut être d'ajouter un en-tête Content-Location sur certaines de vos ressources.
En soit, le comportement des redirections ne semble pas un problème, vous obtenez ce que vous vouliez. Sauf avec Firefox. Celui-ci ne transmet pas les en-têtes que vous avez pu ajouter sur votre requête d'origine. C'est très problématique si votre service sert des contenus différents en fonction de l'en-tête Accept.
Enfin, dans une tradition de n'importe quoi, Internet Explorer juge bon, dans certains cas, de suivre automatiquement les redirections avec la même méthode.
On notera également, que la présence d'un en-tête Location sur une réponse autre que de type 300 n'entraîne heureusement pas de redirection automatique.
Dans l'ensemble, les résultats sont moins dramatiques que ce à quoi je m'attendais. Ils valident même l'utilisation d'un service utilisant pleinement HTTP sans avoir de problème majeur.
Les résultats
| Méthode | Status | FF 3.5 | FF 4.0 | Safari 5.0 | Opera 11.0 | IE 6.0 SP2 | IE 7.0 | IE 8.0 |
|---|---|---|---|---|---|---|---|---|
| GET | 200 | ok | ok | ok | ok | ok | ok | ok |
| POST | 200 | ok | ok | ok | ok | ok | ok | ok |
| PUT | 200 | ok | ok | ok | ok | ok | ok | ok |
| DELETE | 200 | ok | ok | ok | ok | ok | ok | ok |
| POST | 201 | ok | ok | ok | ok | ok | ok | ok |
| PUT | 201 | ok | ok | ok | ok | ok | ok | ok |
| POST | 202 | ok | ok | ok | ok | ok | ok | ok |
| PUT | 202 | ok | ok | ok | ok | ok | ok | ok |
| POST | 203 | ok | ok | ok | ok | ok | ok | ok |
| PUT | 203 | ok | ok | ok | ok | ok | ok | ok |
| POST | 204 | ok | ok | ok | ok | ok | ok | ok |
| PUT | 204 | ok | ok | ok | ok | ok | ok | ok |
| DELETE | 204 | ok | ok | ok | ok | ok | ok | ok |
| GET | 400 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 400 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 400 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 400 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 403 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 403 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 403 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 403 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 404 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 404 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 404 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 404 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 405 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 405 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 405 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 405 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 406 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 406 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 406 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 406 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 409 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 409 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 409 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 409 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 500 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 500 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 500 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 500 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 503 | ok | ok | ok | (2) | ok | ok | ok |
| POST | 503 | ok | ok | ok | (2) | ok | ok | ok |
| PUT | 503 | ok | ok | ok | (2) | ok | ok | ok |
| DELETE | 503 | ok | ok | ok | (2) | ok | ok | ok |
| GET | 301 | (1) | (1) | ok | ok | ok | ok | ok |
| POST | 301 | (1) | (1) | ok | ok | ok | ok | ok |
| PUT | 301 | (1) | (1) | ok | ok | (3) | (3) | (3) |
| DELETE | 301 | (1) | (1) | ok | ok | (3) | (3) | (3) |
| GET | 302 | (1) | (1) | ok | ok | ok | ok | ok |
| POST | 302 | (1) | (1) | ok | ok | ok | ok | ok |
| PUT | 302 | (1) | (1) | ok | ok | (3) | (3) | (3) |
| DELETE | 302 | (1) | (1) | ok | ok | (3) | (3) | (3) |
| GET | 303 | (1) | (1) | ok | ok | ok | ok | ok |
| POST | 303 | (1) | (1) | ok | ok | ok | ok | ok |
| PUT | 303 | (1) | (1) | ok | ok | ok | ok | ok |
| DELETE | 303 | (1) | (1) | ok | ok | ok | ok | ok |
- (1) Les en-tête HTTP ne sont pas transmis par Firefox lors d'une redirection. Un bug est ouvert depuis 2010 #553888
- (2) Avec Opera le statut de la réponse XHR est 0 si les ressources ont un statut de type 400 ou 500 et n'ont pas de contenu.
- (3) Une redirection 301 ou 302 depuis une autre méthode que POST sera suivie avec la même méthode HTTP. Par exemple DELETE avec pour réponse 301 engendrera un DELETE sur la valeur de l'en-tête Location.