> ## 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: strong parameters no Rails 8

> O Rails 8 adiciona o params.expect para permitir e exigir parâmetros em uma única etapa, validar seu formato e evitar um NoMethodError em caso de entrada inesperada.

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="Use params.expect para strong parameters" description="O Rails 8 adiciona o params.expect para permitir e exigir parâmetros em uma única etapa, validar seu formato e evitar um NoMethodError em caso de entrada inesperada." presenter="Chris Oliver" duration="PT8M44S" republished="2026-09-19" />

Apresentado por Chris Oliver para a [GoRails](https://gorails.com). Republicado na AppSignal em 19 de setembro de 2026.

Os scaffolds do Rails 8 geram `params.expect` onde antes geravam `require` e `permit`. A nova forma é mais curta. Ela também corrige um problema de ordem. Esse problema gera exceções evitáveis em aplicações Rails desde que os strong parameters existem.

<h2 id="what-this-covers">
  O que este tutorial cobre
</h2>

* A exceção que `require` seguido de `permit` gera com uma entrada adulterada.
* Por que inverter a ordem corrige o problema, e por que `expect` é melhor do que fazer isso você mesmo.
* Como declarar um parâmetro como array para que ele não possa chegar como hash.

<h2 id="requirements">
  Requisitos
</h2>

| O que | Valor |
| - | - |
| Rails | 8.0 ou posterior |
| Adoção | Opt-in. `require` e `permit` continuam funcionando |

<h2 id="the-problem-with-require-then-permit">
  O problema com require seguido de permit
</h2>

A forma conhecida é:

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

`require(:post)` retorna o que estiver naquela chave e garante apenas que ela esteja presente. `permit` é então chamado sobre o resultado.

Isso funciona bem enquanto `post` é um hash. Não funciona quando alguém envia `?post=hello`. `require` retorna sem problemas a string `"hello"`, `permit` é chamado sobre uma `String`, e a requisição falha com:

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

Um `NoMethodError` é o resultado errado. Os parâmetros estavam malformados, o que é um erro do cliente, e os strong parameters existem exatamente para rejeitar entradas malformadas. Em vez disso, é o código que deveria higienizar a entrada que quebra. Bots que sondam uma aplicação encontram isso rapidamente, e é por isso que esses erros aparecem em volume no monitoramento de erros.

<h2 id="why-reversing-the-order-fixes-it">
  Por que inverter a ordem corrige o problema
</h2>

Chame `permit` primeiro, e o formato é verificado antes que qualquer coisa seja extraída:

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

Agora `permit` avalia `post` em relação a um formato declarado: “um hash que pode conter `title` e `body`”. Uma string não corresponde a isso, então é descartada em vez de repassada. `require` então gera `ActionController::ParameterMissing`, que o Rails já trata como um `400`.

<h2 id="what-expect-does">
  O que o expect faz
</h2>

`params.expect` é essa ordem, já embutida:

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

Uma única chamada. Uma entrada malformada gera `ParameterMissing` em vez de `NoMethodError`, sem que você precise lembrar em qual ordem escrever os dois métodos.

<h2 id="declaring-arrays">
  Declarando arrays
</h2>

O segundo problema que `expect` corrige não pode ser expresso com `permit` de forma alguma.

Considere categorias aninhadas. Com `permit`, enviar `post[categories][name]=foo` produz um único hash, quando seu código espera um array deles. Os parâmetros são permitidos, os tipos estão errados, e a falha aparece mais tarde, em outro lugar.

`expect` permite exigir um array, usando um segundo par de colchetes:

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

Os colchetes externos dizem “isto é um array”. Os internos listam os atributos permitidos em cada elemento. Envie um hash onde um array foi declarado, e ele não é permitido. Ele não chega silenciosamente no formato errado.

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

`expect` é opt-in, e `require` e `permit` não estão descontinuados. Praticamente toda aplicação Rails existente os utiliza, então eles vão continuar existindo por muito tempo.

* **No Rails 8:** migre seus controllers para `expect`. O próprio Rails migrou seus controllers internos, incluindo o Action Mailbox, na mesma mudança. A alteração é mecânica. Substitua `require` e `permit` por `expect`, e adicione um segundo par de colchetes em torno de tudo que deveria ser um array.
* **Ainda não no Rails 8:** escreva `permit` antes de `require`. Você ganha o benefício da ordem sem precisar fazer o upgrade.

<h2 id="what-this-changes-in-your-error-tracking">
  O que isso muda no seu monitoramento de erros
</h2>

Este é um dos poucos refactorings com um efeito diretamente visível na sua lista de issues.

`NoMethodError: undefined method 'permit' for an instance of String` é ruído. Ele vem de quem estiver sondando sua aplicação, não dos seus usuários. Ele não carrega nenhuma informação depois da primeira ocorrência, e disputa atenção com regressões reais. Migrar para `expect` transforma esses erros em `ParameterMissing`, que o Rails responde com um `400` sem propagar pela sua aplicação.

Duas coisas valem a pena fazer junto com a mudança:

* Acompanhe a issue no [monitoramento de erros](/errors) para confirmar que ela para depois do deploy. Adicione um [marcador de deploy](/guides/deploy-markers) para que o antes e depois fique visível no gráfico. Os [parâmetros da requisição](/guides/custom-data/request-parameters) em cada issue mostram o que foi enviado. Você pode usar isso para diferenciar uma sondagem de um formulário que sua própria aplicação envia.
* Se ainda restar algum volume, [ignore o erro](/guides/filter-data/ignore-errors) em vez de deixá-lo parado na sua lista. Ignore-o porque você já estabeleceu que se trata de ruído do lado do cliente, não porque ele é frequente.

<h2 id="transcript">
  Transcrição
</h2>

Transcrito do vídeo e levemente editado. As legendas automáticas entenderam errado vários nomes de produtos e de APIs, e esses foram corrigidos.

<Accordion title="Ler a transcrição">
  **0:02** Olá, pessoal, neste episódio vamos falar sobre o novíssimo `params.expect` no Rails 8, que você talvez já tenha ou ainda não tenha adotado. Você vai ver isso nos seus scaffolds, onde aparece `params.expect` para o ID, e também `params.expect` para o nome do seu model, como post com title e body. E isso resolve alguns problemas da forma antiga de fazer isso. Então vamos dar uma olhada em alguns desses problemas. A forma antiga de fazer isso seria dizer require post, permit, os atributos title e body, certo?

  **0:35** Se entrarmos na nossa aplicação Rails, aqui eu peguei a nova action e estou apenas exibindo os parâmetros que são permitidos ali. Agora vemos que `params.expect` está gerando um erro dizendo que não há post. Agora vamos usar `params.require`. Isso também vai dizer que é inválido porque não há post nos parâmetros. Então vamos adicionar post, e normalmente fazemos algo assim, onde um atributo como title fica entre colchetes ao lado de post, para que ele saiba colocar isso dentro de post e transformar isso em um hash.

  **1:10** No entanto, frequentemente temos usuários maliciosos, bots ou qualquer coisa do tipo bisbilhotando e alterando coisas. E se dissermos post igual a hello, sendo uma string em vez de um hash, nossos parâmetros vão explodir com esse erro de método inexistente, em vez de realmente higienizar esses parâmetros como deveria ser. Então esse é um problema em que temos `params.require` post. Isso vai nos dar, vamos ampliar isso um pouco, isso vai nos retornar a string hello, e obviamente permit não é um método nessa string.

  **1:42** Ele precisa estar em um objeto de parâmetros. Então a forma de corrigir isso é na verdade inverter a ordem. Podemos fazer isso dizendo `params.permit` post com title e body. E também dizemos a ele para fazer esse require de post. Para testar isso, vamos ver se esse require falha com a mensagem de erro correta, o erro de parâmetro ausente.

  **2:13** E isso acontece porque quando `params.permit` olha para isso, ele vai avaliar post e ver que, ah, isso precisa ser um hash em que title é opcional dentro dele. E isso é uma string, não um hash. Então não devemos permitir esse parâmetro. E aí está, obtemos um item vazio aqui, o que então nos permite exigir post. Mas todos os nossos scaffolds e controllers fizeram o oposto desde que os strong parameters existem: eles fizeram o require primeiro.

  **2:47** E esse é um dos problemas que `params.expect` resolve para nós. Se executarmos isso, vamos obter automaticamente esse método de parâmetro ausente, ou parâmetro post ausente, com o erro de parâmetro ausente, exatamente como vemos aqui de forma padrão. Mas não precisamos especificar esse require duas vezes. Ele vai simplesmente cuidar de toda essa parte para nós, o que é ótimo. Então isso simplifica um pouco as coisas e torna isso um pouco mais seguro, de forma que pessoas adulterando parâmetros vão receber os erros corretos de requisições inválidas.

  **3:29** Então isso vai higienizar as coisas de forma um pouco mais tranquila. Mas o que acontece quando temos algo como um item aninhado, por exemplo categories, e categories poderia ter um name dentro dele? Se fizermos isso e dissermos post com, digamos, title e post categories como foo, isso vai constatar que categories não era um hash, era apenas uma string. Então isso não é permitido nesses parâmetros.

  **4:01** Mas se dissermos categories name foo, isso vai definir categories para esses parâmetros permitidos filhos. Mas isso está errado, porque queremos um array em vez de um único item. E então nossos parâmetros ainda são algo que pode ser um pouco adulterado aqui, porque precisamos daqueles colchetes vazios internos para dizer ao Rack e ao Action Dispatch que isso precisa ser interpretado como um array em vez de um objeto, como um hash. Então isso é algo que não é realmente possível fazer no params.permit, mas podemos fazer isso aqui. E a forma de fazer isso é dizer categories e usar colchetes duplos, de forma que o primeiro colchete diga, olha, isso vai ser um array, e depois os atributos permitidos dentro desse array para cada um dos objetos.

  **4:59** Então, se atualizarmos isso agora, vamos obter a coisa correta, com colchetes para a categoria. Então obtemos um array. Se tentássemos adulterar isso e atribuir categories a um hash em vez disso, isso vai dizer que não é permitido, e resolver isso completamente. E você vai ver que, se reativarmos a forma antiga de fazer isso, categories ainda vai ser definido, mas para um único item em vez de um array, o que não é o que queremos. Isso não está sendo validado e higienizado corretamente aqui. Então params.expect é uma ótima melhoria só para tornar isso mais consistente e menos adulterável.

  **5:41** Então precisamos fazer isso corretamente para obter esses tipos. Precisa ser um hash, um array ou um valor individual, e podemos dizer isso a ele, e ele vai impor isso como deveria. Agora uma coisa que quero destacar antes de terminarmos é que params.expect é opt-in, os controllers da sua aplicação Rails existente não precisam usar isso. permit e require provavelmente vão continuar existindo por muito tempo, porque praticamente todas as aplicações Rails os utilizam atualmente, e seria uma mudança muito grande exigir que todos migrassem para expect. E, embora permit e require continuem funcionando, params.expect é realmente a forma recomendada de fazer isso, só para manter as coisas um pouco mais seguras nas suas aplicações Rails. Então, se você puder atualizar para o Rails 8, definitivamente atualize seus controllers para a nova sintaxe, e se você ainda não puder atualizar para o Rails 8, use permit e depois require nessa ordem, para que você ganhe um pouco mais desse benefício de ordem de execução ao permitir esses parâmetros.

  **6:46** Então, quando pessoas fazem esses post igual a name ou foo maliciosos, ou qualquer coisa do tipo, e enviam uma string em vez de um hash ou array, você vai estar protegido por isso, e vai ter menos erros no seu monitoramento de erros, e isso vai ser tratado automaticamente para essas situações, o que é ótimo. Então eu recomendo fortemente fazer pelo menos isso, mas se você puder chegar ao Rails 8, cuide de todos esses métodos de parâmetros e garanta que use params.expect em todos os lugares que puder. Se você quiser ver alguns detalhes internos disso, Martin Emde, que trabalha no RubyGems e no Bundler, criou esse PR para o Rails em abril, que finalmente foi mesclado em setembro, e há muita discussão boa sobre como isso deveria funcionar, e os idas e vindas sobre os vários pontos, porque essa é uma funcionalidade bem central do Rails que está sendo alterada nesse momento. Então é um PR bem grande aqui, e ele atualiza toda a Action Mailbox e outros controllers internos dentro do Rails para também usar params.expect, e tudo isso é bem simples, na verdade.

  **7:59** É basicamente adicionar expect, remover require e permit, e depois envolver quaisquer desses arrays com um colchete extra para lidar com isso. Mas dê uma olhada nesse PR, é realmente uma ótima discussão se você quiser ver o que acontece em uma funcionalidade central como essa pela equipe principal do Rails. Então é isso, params.expect eu acho que vai ser uma ótima melhoria, só protegendo nosso código um pouco mais, e vamos nos beneficiar disso com menos desses problemas de método permit indefinido sempre que alguém estiver bisbilhotando onde não deveria. Então é isso para este episódio, espero que tenham gostado, e nos falamos no próximo. Paz!
</Accordion>

<h2 id="related-tutorials">
  Tutoriais relacionados
</h2>

* [Parâmetros com valores padrão](/tutorials/ruby/parameters-with-defaults)
* [Impersonation](/tutorials/ruby/impersonation)

<h2 id="about-this-tutorial">
  Sobre este tutorial
</h2>

Este tutorial resume um screencast da GoRails sobre `params.expect` no Rails 8, de Chris Oliver. O screencast é a obra original. A GoRails o publica, junto com o resto da série, em [gorails.com](https://gorails.com).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.