> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# params.expect : les strong parameters dans Rails 8

> Rails 8 ajoute params.expect pour autoriser et exiger des paramètres en une seule étape, valider leur forme et éviter une NoMethodError en cas d'entrée inattendue.

export const YouTube = ({id, title, description, presenter, duration, republished}) => {
  const playlistId = "PLFHQSOKTqHXA";
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  if (!id || id === "YOUTUBE_ID") {
    return <div style={{
      border: "1px dashed currentColor",
      borderRadius: "0.5rem",
      opacity: 0.7,
      padding: "2rem 1.5rem",
      textAlign: "center",
      fontSize: "0.875rem"
    }}>
        <strong>Video not published yet.</strong>
        <br />
        {title ? `"${title}" has no YouTube id.` : "This tutorial has no YouTube id."}{" "}
        Replace <code>YOUTUBE_ID</code> in this page with the id from the
        video's URL, once it is in the{" "}
        <a href={playlistUrl}>GoRails x AppSignal playlist</a>.
      </div>;
  }
  const prettyDate = republished ? new Date(`${republished}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  }) : null;
  const schema = {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    name: title,
    embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,
    contentUrl: `https://www.youtube.com/watch?v=${id}`,
    thumbnailUrl: [`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`],
    creator: {
      "@type": "Organization",
      name: "GoRails",
      url: "https://gorails.com"
    },
    publisher: {
      "@type": "Organization",
      name: "AppSignal",
      url: "https://appsignal.com"
    }
  };
  if (description) schema.description = description;
  if (duration) schema.duration = duration;
  if (republished) schema.uploadDate = republished;
  if (presenter) schema.author = {
    "@type": "Person",
    name: presenter
  };
  return <div style={{
    marginBottom: "1.5rem"
  }}>
      <iframe width="100%" height="450" src={`https://www.youtube-nocookie.com/embed/${id}?list=${playlistId}`} title={title} style={{
    borderRadius: "0.5rem",
    border: 0,
    display: "block"
  }} allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />
      <p style={{
    fontSize: "0.875rem",
    marginTop: "0.5rem"
  }}>
        <a href={`https://www.youtube.com/watch?v=${id}&list=${playlistId}`}>{`Watch "${title}" on YouTube`}</a>
      </p>
      <script type="application/ld+json" dangerouslySetInnerHTML={{
    __html: JSON.stringify(schema)
  }} />
    </div>;
};

<YouTube id="Q_0OpE_rcVY" title="Utiliser params.expect pour les strong parameters" description="Rails 8 ajoute params.expect pour autoriser et exiger des paramètres en une seule étape, valider leur forme et éviter une NoMethodError en cas d'entrée inattendue." presenter="Chris Oliver" duration="PT8M44S" republished="2026-09-19" />

Présenté par Chris Oliver pour [GoRails](https://gorails.com). Republié sur AppSignal le 19 septembre 2026.

Les scaffolds de Rails 8 génèrent `params.expect` là où ils généraient auparavant `require` et `permit`. La nouvelle forme est plus courte. Elle corrige aussi un problème d'ordre qui provoque des exceptions évitables dans les applications Rails depuis que les strong parameters existent.

<h2 id="what-this-covers">
  Ce que couvre ce tutoriel
</h2>

* L'exception que déclenchent `require` puis `permit` en cas d'entrée altérée.
* Pourquoi inverser l'ordre corrige le problème, et pourquoi `expect` est préférable à le faire soi-même.
* Comment déclarer un paramètre comme un tableau pour qu'il ne puisse pas arriver sous forme de hash.

<h2 id="requirements">
  Prérequis
</h2>

| Élément  | Valeur                                                  |
| -------- | ------------------------------------------------------- |
| Rails    | 8.0 ou une version ultérieure                           |
| Adoption | Opt-in. `require` et `permit` continuent de fonctionner |

<h2 id="the-problem-with-require-then-permit">
  Le problème avec require puis permit
</h2>

La forme habituelle s'écrit :

```ruby theme={null}
params.require(:post).permit(:title, :body)
```

`require(:post)` renvoie ce qui se trouve à cette clé et vérifie seulement que c'est présent. `permit` est ensuite appelé sur le résultat.

Cela fonctionne tant que `post` est un hash. Cela ne fonctionne plus si quelqu'un envoie `?post=hello`. `require` renvoie sans problème la chaîne `"hello"`, `permit` est appelé sur un `String`, et la requête échoue avec :

```
NoMethodError: undefined method `permit' for an instance of String
```

Une `NoMethodError` n'est pas le bon résultat. Les paramètres étaient malformés, ce qui est une erreur côté client, et les strong parameters existent précisément pour rejeter les entrées malformées. C'est pourtant le code chargé de nettoyer l'entrée qui plante. Les bots qui sondent une application trouvent cela rapidement, ce qui explique pourquoi ces erreurs apparaissent en volume dans le suivi des erreurs.

<h2 id="why-reversing-the-order-fixes-it">
  Pourquoi inverser l'ordre corrige le problème
</h2>

Appelez `permit` en premier, et la forme est vérifiée avant même qu'on extraie quoi que ce soit :

```ruby theme={null}
params.permit(post: [:title, :body]).require(:post)
```

Désormais, `permit` évalue `post` par rapport à une forme déclarée : « un hash qui peut contenir `title` et `body` ». Une chaîne de caractères ne correspond pas, elle est donc écartée plutôt que transmise, et `require` déclenche alors `ActionController::ParameterMissing`, que Rails traite déjà comme un `400`.

<h2 id="what-expect-does">
  Ce que fait expect
</h2>

`params.expect` intègre cet ordre directement :

```ruby theme={null}
params.expect(post: [:title, :body])
```

Un seul appel. Une entrée malformée déclenche `ParameterMissing` plutôt que `NoMethodError`, sans que vous ayez à vous souvenir dans quel ordre écrire les deux méthodes.

<h2 id="declaring-arrays">
  Déclarer des tableaux
</h2>

Le second problème que corrige `expect` ne peut pas s'exprimer du tout avec `permit`.

Prenons des catégories imbriquées. Avec `permit`, envoyer `post[categories][name]=foo` produit un unique hash, alors que votre code attend un tableau de ceux-ci. Les paramètres sont autorisés, les types sont incorrects, et l'échec n'apparaît que plus tard, ailleurs.

`expect` vous permet d'exiger un tableau, à l'aide d'une seconde paire de crochets :

```ruby theme={null}
params.expect(post: [:title, :body, categories: [[:name]]])
```

Les crochets extérieurs indiquent « ceci est un tableau ». Les crochets intérieurs listent les attributs autorisés sur chaque élément. Envoyez un hash là où un tableau était déclaré, et il n'est pas autorisé, au lieu d'arriver silencieusement dans la mauvaise forme.

<h2 id="migrating">
  Migrer
</h2>

`expect` est opt-in, et `require` et `permit` ne sont pas dépréciés. Presque toutes les applications Rails existantes les utilisent, elles resteront donc en usage encore longtemps.

* **Sur Rails 8 :** faites passer vos contrôleurs à `expect`. Rails a lui-même migré ses propres contrôleurs internes, y compris Action Mailbox, dans le même changement. La modification est mécanique : remplacez `require` et `permit` par `expect`, et ajoutez une seconde paire de crochets autour de tout ce qui doit être un tableau.
* **Pas encore sur Rails 8 :** écrivez `permit` avant `require`. Vous obtenez le bénéfice de l'ordre sans passer par la mise à niveau.

<h2 id="what-this-changes-in-your-error-tracking">
  Ce que cela change dans votre suivi des erreurs
</h2>

C'est l'un des rares refactorings dont l'effet est directement visible sur votre liste d'incidents.

`NoMethodError: undefined method 'permit' for an instance of String` est du bruit. Cela provient de qui que ce soit qui sonde votre application, pas de vos utilisateurs. Cela ne fournit plus aucune information après la première occurrence, et cela entre en concurrence pour l'attention avec de vraies régressions. Passer à `expect` transforme ces erreurs en `ParameterMissing`, à laquelle Rails répond par un `400` sans que cela remonte dans votre application.

Deux choses valent la peine d'être faites en parallèle de ce changement :

* Observez l'incident dans le [suivi des erreurs](/errors) pour confirmer qu'il s'arrête après le déploiement. Ajoutez un [marqueur de déploiement](/guides/deploy-markers) pour que la comparaison avant/après soit visible sur le graphique. Les [paramètres de requête](/guides/custom-data/request-parameters) de chaque incident montrent ce qui a été soumis, ce qui permet de distinguer une sonde d'un formulaire envoyé par votre propre application.
* S'il subsiste du volume, [ignorez l'erreur](/guides/filter-data/ignore-errors) plutôt que de la laisser traîner dans votre liste. Ignorez-la parce que vous avez établi qu'il s'agit de bruit côté client, pas parce qu'elle est fréquente.

<h2 id="transcript">
  Transcription
</h2>

Transcrit depuis 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.

<Accordion title="Lire la transcription">
  **0:02** Salut à tous, dans cet épisode on va parler du tout nouveau `params.expect` dans Rails 8, que vous avez peut-être ou pas encore adopté. Vous verrez cela dans vos scaffolds, où vous obtenez `params.expect` pour l'ID, et aussi `params.expect` pour le nom de votre modèle, comme post avec title et body. Et cela résout plusieurs problèmes de l'ancienne façon de faire. Regardons donc certains de ces problèmes. L'ancienne façon de faire serait de dire require post, permit, les attributs title et body, d'accord ?

  **0:35** Si on va dans notre application Rails, ici j'ai pris la nouvelle action et j'affiche simplement les paramètres qui passent. Là, on voit que `params.expect` déclenche une erreur disant qu'il n'y a pas de post. Utilisons maintenant `params.require`. Cela dira aussi que c'est invalide parce qu'il n'y a pas de post dans les paramètres. Ajoutons donc post, et normalement on fait quelque chose comme ceci, où un attribut comme title est entre crochets à côté de post, pour qu'il sache le placer à l'intérieur de post et en faire un hash.

  **1:10** Cependant, on a souvent des utilisateurs malveillants ou des bots ou autre qui viennent fouiller et modifier les choses. Et si on dit post égale hello, alors que c'est une chaîne au lieu d'un hash, nos paramètres vont exploser avec cette erreur de méthode inexistante, au lieu de vraiment nettoyer ces paramètres comme prévu. C'est donc un problème où on a `params.require` post. Cela va nous donner, agrandissons un peu ça, cela va nous renvoyer la chaîne hello, et évidemment permit n'est pas une méthode sur cette chaîne.

  **1:42** Cela doit être appliqué à un objet de paramètres. La façon de corriger cela, c'est en fait d'inverser l'ordre. On peut faire ça en disant `params.permit` post avec title et body. Et on lui dit aussi de faire ce require sur post. Pour essayer ça, on va voir si ce require échoue avec le bon message d'erreur, l'erreur de paramètre manquant.

  **2:13** Et c'est parce que quand `params.permit` regarde cela, il va évaluer post et voir que, ah, cela doit être un hash où title est optionnel à l'intérieur. Et c'est une chaîne, pas un hash. On ne devrait donc pas autoriser ce paramètre. Et voilà, on obtient un élément vide ici, ce qui nous permet ensuite d'exiger post. Mais tous nos scaffolds et contrôleurs ont fait l'inverse depuis que les strong parameters existent : ils ont fait le require en premier.

  **2:47** Et c'est l'un des problèmes que `params.expect` résout pour nous. Si on exécute ça, on obtient automatiquement cette méthode de paramètre manquant, ou paramètre post manquant avec l'erreur de paramètre manquant, exactement comme on le voit ici par défaut. Mais on n'a pas besoin de spécifier ce require en double. Il va simplement gérer toute cette partie pour nous, ce qui est très bien. Cela simplifie donc un peu les choses et les rend un peu plus sûres, si bien que les personnes qui manipulent les paramètres reçoivent les bonnes erreurs pour des requêtes invalides.

  **3:29** Cela va donc nettoyer les choses un peu plus proprement. Mais que se passe-t-il quand on a quelque chose comme un élément imbriqué, par exemple categories, et categories pourrait avoir un name dedans ? Si on fait ça et qu'on dit post avec, disons, title et post categories comme foo, cela va constater que categories n'était pas un hash, mais juste une chaîne. Ce n'est donc pas autorisé dans ces paramètres.

  **4:01** Mais si on disait categories name foo, cela va assigner categories à ces paramètres autorisés imbriqués. Mais c'est incorrect, parce qu'on veut un tableau plutôt qu'un élément unique. Et donc nos paramètres restent quelque chose qu'on peut manipuler un peu ici, parce qu'il faut ces crochets vides à l'intérieur pour indiquer à Rack et à Action Dispatch que cela doit être analysé comme un tableau plutôt que comme un objet, comme un hash. C'est donc quelque chose qui n'est pas vraiment possible avec params.permit, mais qu'on peut faire ici. Et la façon de le faire, c'est de dire categories et d'utiliser des doubles crochets, si bien que le premier crochet dit, voilà, ceci va être un tableau, puis les attributs autorisés à l'intérieur de ce tableau pour chacun des objets.

  **4:59** Si on rafraîchit maintenant, on obtient la bonne chose, avec des crochets pour la catégorie. On obtient donc un tableau. Si on essayait de manipuler ça et d'assigner categories à un hash à la place, cela dira que ce n'est pas autorisé, et gérera cela complètement. Et vous verrez que si on réactive l'ancienne façon de faire, categories sera toujours défini, mais avec un seul élément au lieu d'un tableau, ce qui n'est pas ce qu'on veut. Ce n'est pas correctement validé ni nettoyé ici. params.expect est donc une belle amélioration pour rendre tout ça plus cohérent et moins manipulable.

  **5:41** Il faut donc le faire correctement pour obtenir ces types. Cela doit être soit un hash, soit un tableau, soit une valeur individuelle, et on peut lui indiquer cela, et cela va l'imposer comme prévu. Une chose que je veux souligner avant de terminer : params.expect est opt-in, les contrôleurs de vos applications Rails existantes n'ont pas à l'utiliser. permit et require vont probablement rester en usage encore longtemps, parce que la quasi-totalité des applications Rails les utilisent actuellement, et ce serait un très grand changement d'obliger tout le monde à passer à expect. Et bien que permit et require continuent de fonctionner, params.expect est vraiment la façon recommandée de faire, pour garder vos applications Rails un peu plus sûres. Donc si vous pouvez passer à Rails 8, mettez définitivement vos contrôleurs à jour vers la nouvelle syntaxe, et si vous ne pouvez pas encore passer à Rails 8, utilisez permit puis require dans cet ordre, pour bénéficier un peu plus de cet avantage d'ordre d'exécution pour autoriser ces paramètres.

  **6:46** Donc quand des personnes malveillantes font ce genre de post égale name ou foo ou autre, et soumettent une chaîne au lieu d'un hash ou d'un tableau, vous serez protégé par cela, et vous aurez moins d'erreurs dans votre suivi des erreurs, et cela sera géré automatiquement pour ces situations, ce qui est très bien. Je recommande donc vivement de faire au moins cela, mais si vous pouvez passer à Rails 8, alors occupez-vous de toutes ces méthodes de paramètres et assurez-vous d'utiliser params.expect partout où vous le pouvez. Si vous voulez voir certains détails internes de tout ça, Martin Emde, qui travaille sur RubyGems et Bundler, a soumis cette pull request à Rails en avril, finalement fusionnée en septembre, et il y a beaucoup de discussions intéressantes sur la façon dont cela devrait fonctionner, avec des allers-retours sur les différents points, parce que c'est une fonctionnalité assez centrale de Rails qu'on modifie là. C'est donc une pull request assez importante, qui met à jour toute la Action Mailbox et d'autres contrôleurs internes de Rails pour qu'ils utilisent aussi params.expect, et tout cela est en fait assez simple.

  **7:59** Il s'agit essentiellement d'ajouter expect, de retirer require et permit, puis d'envelopper les tableaux avec une paire de crochets supplémentaire pour gérer cela. Mais allez voir cette pull request, c'est vraiment une excellente discussion si vous voulez voir ce qui se passe autour d'une fonctionnalité aussi centrale, du côté de l'équipe cœur de Rails. Voilà donc, je pense que params.expect va être une belle amélioration, qui protège juste un peu mieux notre code, et on va en profiter avec moins de ces problèmes de méthode permit indéfinie, chaque fois que quelqu'un vient fouiller là où il ne devrait pas. C'est tout pour cet épisode, j'espère que vous l'avez apprécié, et je vous retrouve dans le prochain. Peace !
</Accordion>

<h2 id="related-tutorials">
  Tutoriels associés
</h2>

* [Paramètres avec valeurs par défaut](/tutorials/ruby/parameters-with-defaults)
* [Impersonation](/tutorials/ruby/impersonation)

<h2 id="about-this-tutorial">
  À propos de ce tutoriel
</h2>

Ce tutoriel résume un screencast GoRails sur `params.expect` dans Rails 8, par Chris Oliver. Le screencast est l'œuvre originale. GoRails le publie, ainsi que le reste de la série, sur [gorails.com](https://gorails.com).
