Les verbes HTTP (spécification HTTP 1.1) correspondent à des méthodes d’appel vers un serveur. Généralement, tous les développeurs connaissent et utilisent le GET et le POST, mais il en existe plusieurs autres, chacun avec son cas d’utilisation.
Il existe plein d’articles sur leur bonne utilisation dans le cadre de services REST (comme ici ou là), mais cet article se concentrera sur le côté pratique avec la mise en place de requêtes CORS en javascript utilisant autre chose que le GET.
Le CORS ?
Une partie des ressources HTTP d’une page (les scripts notamment) sont soumises à une politique de « Same-Origin ». Le principe a été mis en place pour des raisons de sécurité, mais en gros, il est théoriquement impossible d’effectuer une requête depuis un fichier javascript chargé depuis un domaine spécifié vers un autre domaine (plus d’informations chez Mozilla).
Théoriquement, car il existe un paquet de méthodes pour contourner le problème. A une époque pas si lointaine, tout le monde utilisait le jsonp, sorte de hack pour faire des requêtes GET uniquement vers un domaine externe.
Depuis, le CORS est disponible sur pas mal de navigateurs et a même atteint le stade de recommandation du W3C depuis début 2014.
Une requête CORS, c’est une XMLHttpRequest tout ce qu’il y a de plus standard. Le blocage habituel de la Same Origin Policy se règle côté serveur, en ajoutant une entête HTTP aux réponses. Par exemple, dans une configuration apache/htaccess :
Header set Access-Control-Allow-Origin "*"
Voilà, rien de plus simple. Seulement, on peut avoir quelques problèmes quand on veut faire autre chose que du GET.
La requête OPTIONS
Oui, car quand on veut faire du PUT, du DELETE et même parfois du POST, on voit une petite requête OPTIONS passer avant dans la console.
Cette requête, appelée preflight, est un mécanisme mis en place côté navigateur pour décider si la requête est sûre. Et si elle ne renvoie pas un code HTTP 200, la requête originale est annulée.
Son cas d’apparition est parfaitement expliqué dans la doc de Mozilla. Extrait :
[...] methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. [...]
Du coup, il faut que le serveur accepte de répondre aux requêtes OPTIONS. Et manque de bol, les développeurs de services REST ne pensent pas toujours à implémenter ça.
Généralement, chaque service ne répond qu’à une méthode, qu’elle soit GET, POST, PUT, DELETE ou autre. Mais la requête OPTIONS, elle, est la plupart du temps oubliée.
Pour pallier à ceci, il est possible de modifier un peu son htaccess pour ne pas avoir à modifier tout les webservices (ici, en mode complètement ouvert. Vous pouvez restreindre un peu les choses) :
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, PATCH, DELETE"
Header always set Access-Control-Allow-Headers "X-Accept-Charset,X-Accept,Content-Type"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L,E=HTTP_ORIGIN:%{HTTP:ORIGIN}]]
On notera surtout la deuxième partie, qui va renvoyer une 200 pour toute requête OPTIONS vers le serveur.
Cette solution a été trouvée je ne sais plus sur quel page de stackoverflow, mais une réflexion intéressante est également disponible sur ce site.