Cas pratique :
– vous avez un site avec un frontend et un backend
– le backend possède une action et une route qu’il faut appeler avec le verbe HTTP DELETE. Cette action attend un paramètre et supprime un entrée en base de données. Après la suppression, l’action redirige sur la page d’accueil (faute de truc plus intelligent à faire) qui doit être appelée en GET.
– le frontend appelle en AJAX l’adresse HTTP de votre action et reçoit une redirection en réponse.
– le navigateur utilise le verbe de la requête initiale pour suivre la redirection. C’est de drame, votre adresse redirigée n’existe pas en DELETE, si vous utilisez Ruby on Rails vous allez vous manger une erreur 500 "No route matches [DELETE]"
Pour contourner ce comportement par défaut, il faut utiliser le statut 303 en header de réponse de la redirection.
En Rails
Le correctif
1 |
redirect_to edit_user_path(params[:user_id]), status: 303 |
Le cas des link_to
Rails propose de créer des liens HTML vers des URL en DELETE via la fonction link_to
. Cette fonction a la particularité de pouvoir prendre un paramètre method
générant un attribut data-method
qui contient le verbe HTTP souhaité.
L’erreur de redirection décrite dans cet article ne se produit pas dans ce cas précis car un lien HTML sera toujours appelé en GET quoi qu’il arrive. La redirection qui en découle sera donc en GET.
Comment est-ce possible ?
- Le framework est livré avec un Gemfile par défaut contenant jquery-rails. Hors, il se trouve que cette gem contient non seulement jQuery mais aussi jQuery UJS (« Ruby on Rails unobtrusive scripting adapter for jQuery ») qui est ajouté sur chaque page rendue.
- Le JS intercepte tout clic sur un lien et ajoute les attributs data-* en paramètres à la requête HTTP.
- Finalement, Rails analyse les requêtes GET reçues à la recherche d’un paramètre indiquant le verbe HTTP à utiliser, parcoure config/routes.rb à la recherche de la route correspondante et déclenche la chaine d’appel menant à l’action souhaitée.
Un ticket Github indique que désormais, jQuery UJS ne sera plus inclus dans la gem jQuery. Le code a été réécrit en JavaScript vanilla et inclus dans une nouvelle gem appelée rails-ujs.
https://github.com/rails/rails/pull/27113