ActionController::Parameters#with_defaults fournit des valeurs pour les clés absentes de la requête. C’est un alias de reverse_merge : le hash que vous passez perd face à tout ce que le navigateur a soumis.
Il se justifie parce que le navigateur ne soumet parfois rien du tout, dans plusieurs cas courants. Un contrôleur qui traite « absent » et « false » différemment se trompe dans ces cas.
Ce que couvre ce tutoriel
- Définir des valeurs par défaut pour les paramètres que le navigateur peut omettre.
- Comment les cases à cocher Rails résolvent déjà ce problème avec un champ caché.
- Pourquoi une valeur par défaut n’est pas un mécanisme d’autorisation.
Prérequis
Définir une valeur par défaut
status est présent, il est utilisé. S’il ne l’est pas, "draft" l’est. Des cases à cocher non cochées et des champs à choix multiples non sélectionnés sont la raison habituelle pour laquelle une clé manque. Une valeur par défaut est plus soignée qu’une chaîne de conditions dans l’action.
ActionController::Parameters n’est pas un Hash, même s’il se donne du mal pour se comporter comme tel. Il implémente merge, values_at, compact, blank?, et une longue liste d’autres méthodes. with_defaults fait partie de cette surface.
Ce que font déjà les cases à cocher Rails
Avant de recourir à une valeur par défaut pour une case à cocher, il est utile de savoir que Rails s’en occupe généralement déjà. Une case à cocher HTML non cochée ne soumet rien, et un formulaire n’a aucun moyen d’indiquer « ceci n’était pas cochée ».form.check_box génère donc deux champs : un champ caché avec la valeur 0, et la case à cocher elle-même avec la valeur 1.
Les deux sont soumis dans l’ordre du document lorsque la case est cochée, donc Rails reçoit 0 puis 1 et retient le dernier. Lorsqu’elle n’est pas cochée, seul le 0 arrive. Il en résulte qu’une case à cocher Rails soumet toujours quelque chose, et vous avez rarement besoin d’une valeur par défaut pour elle.
Une valeur par défaut n’est pas une autorisation
C’est le point à bien comprendre. Les valeurs par défaut perdent face aux valeurs soumises. C’est ce qui en fait des valeurs par défaut. Une valeur par défaut ne peut donc pas empêcher un utilisateur de définir une valeur : quiconque soumet la clé la remplace. Pour les attributs que l’utilisateur ne doit pas contrôler, comme l’auteur d’un enregistrement, n’exprimez pas cette intention sous forme de valeur par défaut :user_id n’est pas autorisé, donc il ne peut pas du tout être assigné en masse, et la propriété provient de la session plutôt que de la requête. Utilisez with_defaults pour de véritables valeurs par défaut, où il est acceptable qu’un utilisateur remplace la valeur.
Voir ce qui a été soumis
Lorsqu’une valeur par défaut ne se comporte pas comme prévu, la question est presque toujours ce que contenait la requête. En local, vous pouvez le lire dans le panneau réseau. En production, AppSignal enregistre les paramètres de requête sur les samples d’erreur et de performance, afin qu’un incident affiche les paramètres qui l’ont réellement produit plutôt que ceux que vous supposiez. Comme les paramètres contiennent régulièrement des données personnelles, vérifiez ce qui est envoyé. AppSignal permet de filtrer les paramètres afin que les clés sensibles ne soient jamais transmises, et il est utile de configurer cela avant de vous appuyer sur les données de paramètres dans les incidents.Transcription
Transcrit à partir de la vidéo et légèrement édité : les sous-titres automatiques ont mal compris plusieurs noms de produits et d’API, et ceux-ci ont été corrigés.Lire la transcription
Lire la transcription
0:02 Salut à tous, dans cet épisode on va parler des paramètres d’Action Controller, et plus précisément de la méthode ici en bas, with defaults. C’est simplement un alias pour reverse merge, mais c’est une de ces choses que je vois souvent et que je fais moi-même couramment dans nos contrôleurs quand j’ai besoin d’assigner des valeurs par défaut. Regardons donc un exemple. Générons un scaffold pour un modèle post, et on veut que le post appartienne à un utilisateur. On veut aussi que le post ait un titre et un corps qui soit une colonne de type texte.0:41 On va donc créer ça. On lance
rails db:migrate, on ouvre ça dans notre éditeur, et notre contrôleur post ici va générer ça. Mais la partie importante, c’est qu’on veut que ce nouveau post assigne effectivement une valeur par défaut à cette référence user. On pourrait donc dire current user ici, et simplement l’assigner. Mais ce qui serait vraiment bien, c’est de pouvoir faire ça dans le cadre des paramètres qui arrivent, et c’est exactement ce qu’on peut faire.1:12 Donc au lieu de rendre cette ligne ici, dans notre action create, un peu plus compliquée, on peut utiliser with defaults ici, et dire que user va être current user. Bon, je n’ai pas de méthode current user, donc on va juste dire user.first pour ça. Et on ne veut pas que l’utilisateur puisse choisir qui a écrit le post, donc on va s’assurer que user ID n’est pas dans les paramètres autorisés ici. Si on va sur notre formulaire post, on peut se débarrasser de user ID, et ça va essentiellement utiliser ce hash en premier, puis fusionner les champs title et body qui sont arrivés dans les paramètres. On peut aussi utiliser ça pour d’autres choses, par exemple si vous avez une case à cocher ou un tableau ou quelque chose qui peut ou non être soumis par le navigateur, vous pouvez spécifier votre hash de paramètres par défaut ici, et ils pourront ensuite être remplacés par ce qui est soumis par le navigateur.2:11 C’est donc une petite fonctionnalité super cool. Ouvrons maintenant notre serveur Rails, et allons sur slash posts slash new. On peut mettre test et test. Si on crée ce post, on va se voir assigner ici le user ID numéro un grâce à ce with defaults. C’est donc vraiment juste le reverse merge intégré à un hash, même si les paramètres d’Action Controller n’héritent pas réellement d’un hash.2:41 Donc si on va sur GitHub pour ça, vous verrez, si on regarde l’implémentation, qu’il y a toutes ces méthodes qui correspondent à des hashes comme merge, values at, compact, blank, compact, et j’en passe. On a tous ces détails d’implémentation qui font que ça fonctionne comme un hash dans la plupart des cas. Mais si on remonte à la classe et qu’on cherche class Parameters, vous verrez que ça n’hérite pas réellement d’un hash, ça se comporte juste comme tel. C’est donc une petite chose vraiment utile que Colin m’a apprise l’autre jour : en gros, en utilisant with defaults, on peut avoir ça comme un hash de valeurs de base. Si on veut le user ici, on peut le faire.3:29 On pourrait faire ça pour des valeurs par défaut sur des boutons radio ou des cases à cocher, ou peu importe le cas, un tableau. Et c’est simplement un excellent moyen de faire ça si le navigateur ne soumet en fait rien. L’autre option, c’est ce que font les cases à cocher dans le navigateur : si vous avez un F.checkbox dans votre formulaire post, si on écrit ici form.checkboxhello, les cases à cocher ne sont pas soumises au navigateur à moins d’être effectivement cochées. Il n’y a donc aucun moyen de la décocher dans vos formulaires. Check underscore box.4:11 Ce que vous voyez ici, c’est qu’on a ce champ hello qui est une valeur cachée et qui met la valeur à zéro. Donc si ce n’est pas cochée, cette valeur cachée sera soumise, et elles arrivent dans un ordre dans le formulaire. Donc quand c’est cochée, ça envoie la valeur zéro puis la valeur un, et les paramètres Rails sont capables de comprendre que oui, la dernière valeur est celle que vous voulez réellement dans votre application Rails. Donc si on met à jour notre post et qu’on va regarder la requête réseau, on verra que post hello zero a été soumis, mais aussi post hello one. Et si on modifie ce post sans cocher la case, on peut vérifier ce payload.5:02 Post hello zero était juste zéro. Il n’y avait pas de one pour la case cochée parce qu’elle n’était pas cochée. Vous pouvez donc utiliser des champs dans votre HTML pour essayer de faire fonctionner ça pour des valeurs par défaut. Mais s’il y a des cas où vous voulez que le serveur les définisse, par exemple, on veut s’assurer que le current user est défini ici par le serveur et on ne veut pas que l’utilisateur le soumette, alors on peut utiliser la méthode with defaults pour s’en occuper, ce que j’ai trouvé incroyablement utile et incroyablement pratique. Et j’ai été surpris de ne pas l’avoir appris avant.5:38 Je me suis donc dit que ça ferait un screencast parfait à partager avec vous. C’est tout pour cet épisode. J’espère qu’il vous a plu. On se retrouve dans le prochain. Peace.Tutoriels connexes
À propos de ce tutoriel
Ce tutoriel résume un screencast GoRails de Chris Oliver surwith_defaults pour les paramètres d’Action Controller. Le screencast est l’œuvre originale. GoRails le publie, ainsi que le reste de la série, sur gorails.com.