v7.0.8
Veja mais em rubyonrails.org: Mais Ruby on Rails

Action Controller Overview

Nesse guia você irá aprender como controllers trabalham e como eles se encaixam no ciclo de requisições da sua aplicação.

Depois de ler este guia, você irá saber:

1 O que um Controller faz?

ActionController é o C em MVC. Após o router determinar qual controller usar para a requisição, o controller será responsável por entender a requisição e retornar a resposta apropriada. Por sorte, ActionController faz a maior parte do trabalho fundamental pra você e usa convenções inteligentes pra fazer esse processo ser tão intuitivo quanto possível.

Para a maior parte das aplicações RESTful,o controller receberá a requisição (o que é "invisível" a você que está desenvolvendo), busca e/ou salva dados de um model, e usa a view para criar a saída HTML. Se seu controller precisa tratar requisições um pouco diferente, isso não é um problema, este é apenas o jeito mais comum de um controller trabalhar.

Um controller pode então ser pensado como um intermediário entre um model e uma view. Isso faz com que os dados do model fiquem disponíveis para a view, para que possa ser mostrado ao usuário, e ele salva ou atualiza dados do usuário no model.

Para mais detalhes sobre processo de roteamento, veja Rails Routing from the Outside In

2 Convenção para Nomeclatura de Controllers

A convenção para nomenclatura de controllers no Rails favorece a pluralização da última palavra do nome do controller, embora não seja estritamente necessário (ex: ApplicationController). Por exemplo, ClientsController é recomendado ao invés de ClientController, SiteAdminsController é recomendado ao invés de SiteAdminController ou SitesAdminsController, e assim por diante.

Seguindo essa convenção será possível utilizar o gerador de rotas padrão (ex: resources, etc) sem precisar configurar cada :path ou :controller, e ainda manter consistente o uso dos auxiliares de rotas em todo o seu projeto. Veja Layouts e Guia de Renderização para mais detalhes.

A convenção para nomenclatura de controllers difere da convenção para nomenclatura de models, que devem ser nomeados na forma singular.

3 Métodos e Actions

Um controller é uma classe do Ruby que herda de ApplicationController e tem métodos como qualquer outra classe. Quando a sua aplicação recebe uma requisição, o roteamento irá determinar qual controller e qual action serão executados, e então o Rails irá criar uma instância desse controller e executará o método que possui o mesmo nome da action.

class ClientsController < ApplicationController
  def new
  end
end

Como exemplo, se um usuário acessar /clients/new na sua aplicação para adicionar um novo cliente, o Rails irá criar uma instância de ClientsController e irá chamar o método new dele. Repare que o método vazio do exemplo acima funcionaria normalmente porque o Rails por padrão vai renderizar a view new.html.erb a menos que a action indique outro caminho. Ao criar um novo Client o método new pode tornar uma variável de instância @client acessível na view.

def new
  @client = Client.new
end

O Guia de Layouts e Renderização explica essa etapa mais detalhadamente.

O ApplicationController herda de ActionController::Base, que define uma quantidade de métodos úteis. Este guia vai cobrir alguns destes métodos, mas se você tiver curiosidade para ver o que há neles, você pode ver todos eles na Documentação da API ou no próprio código fonte.

Apenas métodos públicos são executáveis como actions. É uma boa prática diminuir a visibilidade de métodos (utilizando private ou protected) que não foram designados para serem actions, como métodos auxiliares ou filtros.

Alguns nomes de métodos são reservados pelo Action Controller. Redefini-los acidentalmente como actions, ou mesmo como métodos auxiliares (helpers), pode resultar no erro SystemStackError. Se você limitar seus controllers a apenas actions RESTful Resource Routing você não precisará se preocupar com isso.

Se você precisar usar um método reservado como um nome de action, uma solução alternativa é usar uma rota personalizada para mapear o nome do método reservado para o método da action não reservado.

4 Parâmetros

Você provavelmente vai querer acessar os dados enviados pelo usuário ou outros parâmetros nas actions do seu controller. Existem dois tipos de parâmetros possíveis numa aplicação web. O primeiro são os parâmetros que são enviados como parte da URL, chamados parâmetros de query string. A query string é tudo o que vem após o "?" na URL. O segundo tipo de parâmetro é geralmente referido como os dados de POST. Essa informação geralmente vem de um formulário HTML que foi preenchido pelo usuário. Se chamam dados de POST porque estes dados somente podem ser enviados como parte de uma requisição HTTP usando o verbo POST. O Rails não faz distinção sobre parâmetros de query string e parâmetros de POST, ambos são acessíveis por meio do hash params no seu controller:

class ClientsController < ApplicationController
  # Essa action usa parâmetros de query string porque ela é
  # executada através de uma requisição HTTP GET, mas isso
  # não faz nenhuma diferença como os parâmetros
  # são acessados. A URL para essa action seria desse jeito
  # para mostrar os clientes ativos: /clients?status=activated
  def index
    if params[:status] == "activated"
      @clients = Client.activated
    else
      @clients = Client.inactivated
    end
  end

  # Essa action usa parâmetros de POST. Eles provavelmente estão
  # vindo de um formulário HTML que o usuário submeteu. A URL
  # para essa requisição RESTful será "/clients", e os dados
  # serão enviados como parte do corpo da requisição.
  def create
    @client = Client.new(params[:client])
    if @client.save
      redirect_to @client
    else
      # Essa linha sobrescreve o método padrão de renderização,
      # que seria chamado para renderizar a *view* "*create*"
      render "new"
    end
  end
end

4.1 Hash e Parâmetros de Array

O hash params não é limitado a um vetor unidimensional de chaves e valores. Ele pode conter arrays e hashes aninhados. Para enviar um array de valores, concatene um par de colchetes vazio "[]" ao nome da chave:

GET /clients?ids[]=1&ids[]=2&ids[]=3

A URL efetiva neste neste exemplo será codificada como "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3", visto que os caracteres "[" e "]" não são permitidos em URLs. Na maioria do tempo você não precisa se preocupar com isso porque o navegador irá codificar os dados para você, e o Rails vai decodificá-los automaticamente, porém se por acaso você se encontrar na situação de ter que enviar este tipo de requisição ao servidor manualmente você deve ter em mente essa questão.

O valor de params[:ids] será neste caso ["1", "2", "3"]. Note que os valores de parâmetros são sempre strings; o Rails não tenta adivinhar ou converter o tipo.

Valores como [nil] ou [nil, nil, ...] em params são substituídos por [] por motivos de segurança por padrão. Veja o Guia de Segurança para mais informações.

Para enviar um hash, você inclui o nome da chave dentro dos colchetes:

<form accept-charset="UTF-8" action="/clients" method="post">
  <input type="text" name="client[name]" value="Acme" />
  <input type="text" name="client[phone]" value="12345" />
  <input type="text" name="client[address][postcode]" value="12345" />
  <input type="text" name="client[address][city]" value="Carrot City" />
</form>

Quando esse formulário é enviado o valor de params[:client] será { "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }. Repare o hash aninhado em params[:client][:address].

O objeto params atua como um hash, mas permite que você use symbols e strings indistintamente como chaves.

4.2 Parâmetros JSON

Se você está construindo uma aplicação web, você pode achar mais confortável receber parâmetros no formato JSON. Se o header "Content-Type" da sua requisição estiver definido como "application/json" o Rails vai automaticamente carregar os seus parâmetros no hash params, que você pode acessar como acessaria normalmente.

Então por exemplo, se você estiver enviando este conteúdo JSON:

{ "company": { "name": "acme", "address": "123 Carrot Street" } }

O seu controller vai receber params[:company] no formato { "name" => "acme", "address" => "123 Carrot Street" }.

Além disso, se você tiver ativado config.wrap_parameters no seu inicializador ou chamado wrap_parameters no seu controller, você pode omitir o elemento raiz no seu parâmetro JSON. Neste caso, os parâmetros serão clonados e enpacotados sob uma chave baseada no nome do seu controller. Então a requisição JSON acima pode ser escrita como:

{ "name": "acme", "address": "123 Carrot Street" }

E, assumindo que você está enviando os dados para CompaniesController, eles serão então encapsulados na chave :company desta maneira:

{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } }

Você pode customizar o nome da chave ou parâmetros específicos que você quer envelopar consultando a documentação da API

Suporte para interpretar parâmetros XML foi extraído para uma gem chamada actionpack-xml_parser.

4.3 Parâmetros de Rota

O hash params sempre irá conter as chaves :controller e :action, mas você deve usar os métodos controller_name e action_name para acessar estes valores. Quaisquer outros parâmetros definidos pela rota, como :id, também estarão disponíveis. Por exemplo, considere uma listagem de clientes onde a lista pode mostrar os clientes ativos e inativos. Nós podemos adicionar uma rota que captura o parâmetro :status numa URL "normalizada":

get '/clients/:status', to: 'clients#index', foo: 'bar'

Neste caso, quando um usuário abrir a URL /clients/active, params[:status] estará definido como "active". Quando esta rota é usada, params[:foo] também será definido como "bar", como se tivesse sido enviado por meio da query string. O seu controller também irá receber params[:action] com o valor "index" e params[:controller] com o valor "clients".

4.4 default_url_options

Você pode determinar parâmetros padrão globais para a geração de URLs definindo um método chamado default_url_options no seu controller. Este método deve retornar um hash com os dados padrão desejados, cujas chaves devem ser símbolos:

class ApplicationController < ActionController::Base
  def default_url_options
    { locale: I18n.locale }
  end
end

Estas opções serão usadas como um ponto de partida na geração de URLs, então é possível que elas sejam sobrescritas pelas opções passadas para chamadas a url_for.

Se você definir default_url_options em ApplicationController, como no exemplo acima, estes padrões irão ser usados para todas as gerações de URL. O método pode também ser definido num controller específico, neste caso afetando somente as URLs geradas a partir desse escopo.

Numa requisição o método não é de fato chamado para toda URL gerada. Por questões de performance o hash retornado é cacheado, e há no máximo uma invocação por requisição.

4.5 Parâmetros Fortes

Com parâmetros fortes (strong parameters), os parâmetros do Action Controller são proibidos de serem usados nas atribuições em massa no Active Model até que sejam deliberadamente permitidos. Isso significa que você tem que tomar uma decisão consciente sobre quais atributos podem ser permitidos para um update em massa. Esta é uma prática mais segura para ajudar a prevenir que acidentalmente os usuários atualizem atributos sensíveis do model.

Além disso, os parâmetros podem ser marcados como obrigatórios e irão seguir por um fluxo de erro e tratamento predefinido que irá resultar num código 400 Bad Request sendo retornado caso todos os parâmetros obrigatórios não forem informados.

class PeopleController < ActionController::Base
  # Isso vai lançar uma exceção do tipo ActiveModel::ForbiddenAttributesError
  # porque está usando atribuição em massa sem passar pela etapa de permitir
  # explicitamente os parâmetros.
  def create
    Person.create(params[:person])
  end

  # Isso irá passar contanto que exista uma chave de *person* nos parâmetros,
  # caso contrário o código irá lançar uma exceção do tipo
  # ActionController::ParameterMissing, que será capturada pelo
  # ActionController::Base e transformada num erro 400 Bad Request.
  def update
    person = current_account.people.find(params[:id])
    person.update!(person_params)
    redirect_to person
  end

  private
    # Usar um método privado para encapsular os parâmetros permissíveis
    # é um bom padrão visto que que você poderá reusar a mesma lista
    # para as actions create e update. Você também pode especificar
    # este método com a checagem de atributos permitidos de acordo com
    # cada usuário.
    def person_params
      params.require(:person).permit(:name, :age)
    end
end
4.5.1 Valores Escalares Permitidos

Chamando permit como:

params.permit(:id)

permite a chave especificada (:id) para inclusão se ela aparecer em params e ela tiver um valor escalar permitido associado a ela. Caso contrário a chave será filtrada, então arrays, hashes, ou quaisquer outros objetos não poderão ser adicionados.

Os tipos escalares permitidos são String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile, e Rack::Test::UploadedFile.

Para declarar que o valor em params deve ser um array de valores escalares permitidos, mapeie a chave para um array vazio.

params.permit(id: [])

Às vezes não é possível ou conveniente declarar as chaves válidas de um parâmetro de hash ou sua estrutura interna. Apenas mapeie para um hash vazio:

params.permit(preferences: {})

entretanto fique atento porque isso abre a porta para input arbitrário. Neste caso, permit garante que os valores na estrutura retornada são valores escalares permitidos e faz a filtragem de tudo o que houver além deles.

Para permitir um hash completo de parâmetros, o método permit! pode ser usado:

params.require(:log_entry).permit!

Este código marca o hash de parâmetros :log_entry e qualquer sub-hash dele como valores permitidos e não verifica por escalares permitidos, sendo qualquer coisa a partir dele aceita. Extremo cuidado deve ser considerado ao usar o método permit!, visto que ele irá permitir que todos os atuais e futuros atributos do model sejam preenchidos em massa.

4.5.2 Parâmetros Aninhados

Você também pode usar permit em parâmetros aninhados, da seguinte forma:

params.permit(:name, { emails: [] },
              friends: [ :name,
                         { family: [ :name ], hobbies: [] }])

Esta declaração permite o preenchimento dos atributos name, emails, e friends. É esperado que emails seja um array de valores permitidos escalares, e que friends seja um array de recursos com atributos específicos: deve possuir um atributo name (com quaisquer valores escalares permitidos), um atributo hobbies como um array de valores permitidos escalares, e um atributo family que é restrito a ter um name (com qualquer valor escalar permitido também).

4.5.3 Mais Exemplos

Você pode também querer usar os atributos permitidos na sua action new. Isso traz o problema que você não pode chamar require na chave raiz porque normalmente ela não existe no momento da chamada de new

# usando fetch você pode fornecer um valor padrão e visualizar
# a API de Parâmetros Fortes a partir dele.
params.fetch(:blog, {}).permit(:title, :author)

O método da classe model accepts_nested_attributes_for te permite atualizar e destruir outros models associados. Isso é baseado nos parâmetros id e _destroy:

# permite :id e :_destroy
params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy])

Hashes com chaves de valor do tipo inteiro são tratados de maneira diferente, e você pode declarar os atributos como se eles fossem atributos filhos imediatos. Você obtém estes tipos de parâmetros quando você usa accepts_nested_attributes_for combinado com uma associação has_many:

# Para permitir os seguintes dados:
# {"book" => {"title" => "Some Book",
#             "chapters_attributes" => { "1" => {"title" => "First Chapter"},
#                                        "2" => {"title" => "Second Chapter"}}}}
params.require(:book).permit(:title, chapters_attributes: [:title])

Imagine um cenário onde você tem parâmetros representando um nome de produto e um hash de dados arbitrários associado a esse produto, e você queira permitir o preenchimento do atributo de nome do produto e também o hash de dados:

def product_params
  params.require(:product).permit(:name, data: {})
end
4.5.4 Fora do Escopo de Parâmetros Fortes

A API de parâmetros fortes foi desenhada com os casos mais comuns em mente. Não houve a intenção de torná-la uma bala prateada para lidar com todos os seus problemas de filtragem de parâmetros. Entretanto, você pode facilmente misturar a API com seu próprio código para se adaptar à sua situação.

5 Sessão

Sua aplicação possui uma sessão para cada usuário, na qual pode-se armazenar quantidades pequenas de dados que serão persistidos entre as requisições. A sessão fica disponível apenas no controller e na view e pode utilizar um dentre vários mecanismos diferentes de armazenamento:

Todos os armazenamentos de sessão utilizam um cookie para armazenar um ID único para cada sessão (você deve utilizar um cookie, o Rails não permitirá que você passe o ID da sessão na URL, pois isso é menos seguro).

Para a maioria dos armazenamentos, esse ID é utilizado para procurar os dados da sessão no servidor, por exemplo, em uma tabela do banco de dados. Há apenas uma exceção, que é o armazenamento de sessão recomendado por padrão - o CookieStore - que armazena todos os dados da sessão no próprio cookie (o ID ainda estará disponível para você, se você precisar). A vantagem é a de ser muito leve e requer zero configuração em uma nova aplicação para utilizar a sessão. Os dados do cookie são assinados criptograficamente para torná-los invioláveis, e também é criptografado para que qualquer pessoa com acesso não leia o seu conteúdo. (O Rails não aceitará se estiver sido editado).

O CookieStore pode armazenar cerca de 4 kB de dados - muito menos que os demais - mas geralmente é o suficiente. O armazenamento de grandes quantidades de dados na sessão não é recomendado, independentemente de qual armazenamento de sessão sua aplicação utiliza. Você deve evitar armazenar objetos complexos (como instâncias de model) na sessão, pois o servidor pode não ser capaz de remontá-los entre as requisições, o que resultará em um erro.

Se as suas sessões de usuário não armazenam dados críticos ou não precisam durar por longos períodos (por exemplo, se você apenas utiliza o flash para mensagens), considere o uso do ActionDispatch::Session::CacheStore. Isso armazenará as sessões utilizando a implementação de cache que você configurou para a sua aplicação. A vantagem é que você pode utilizar sua infraestrutura de cache existente para armazenar sessões sem precisar de nenhuma configuração ou administração adicional. A desvantagem é que as sessões serão temporárias e poderão desaparecer a qualquer momento.

Leia mais sobre armazenamento de sessão no Guia de Segurança.

Se você precisar de um mecanismo diferente de sessão de armazenamento, você poderá alterá-lo no initializer:

# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails g active_record:session_migration")
# Rails.application.config.session_store :active_record_store

O Rails configura uma chave de sessão (o nome do cookie) ao assinar os dados da sessão. Estes também podem ser alterados no initializer:

# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_your_app_session'

Você também pode passar uma chave :domain e especificar o nome do domínio para o cookie:

# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"

O Rails configura (para o CookieStore) uma chave secreta utilizada para assinar os dados da sessão em config/credentials.yml.enc. Isso pode ser alterado com o comando bin/rails credentials:edit.

# aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 492f...

Alterar a secret_key_base ao utilizar o CookieStore invalidará todas as sessões existentes.

5.1 Acessando a Sessão

No seu controller, você pode acessar a sessão através do método de instância session.

As sessões são lazy loading (carregamento lento). Se você não acessá-las no código da sua action, elas não serão carregadas. Portanto, você nunca precisará desativar as sessões, basta apenas não acessá-las.

Os valores da sessão são armazenados utilizando pares de chave/valor como em um hash:

class ApplicationController < ActionController::Base

  private

  # Finds the User with the ID stored in the session with the key
  # :current_user_id This is a common way to handle user login in
  # a Rails application; logging in sets the session value and
  # logging out removes it.
  def current_user
    @_current_user ||= session[:current_user_id] &&
      User.find_by(id: session[:current_user_id])
  end
end

Para armazenar algo na sessão, basta atribuí-lo à chave como em um hash:

class LoginsController < ApplicationController
  # "Create" a login, aka "log the user in"
  def create
    if user = User.authenticate(params[:username], params[:password])
      # Save the user ID in the session so it can be used in
      # subsequent requests
      session[:current_user_id] = user.id
      redirect_to root_url
    end
  end
end

Para remover algo da sessão, exclua o par de chave/valor:

class LoginsController < ApplicationController
  # "Delete" a login, aka "log the user out"
  def destroy
    # Remove the user id from the session
    session.delete(:current_user_id)
    # Clear the memoized current user
    @_current_user = nil
    redirect_to root_url, status: :see_other
  end
end

Para redefinir a sessão inteira, utilize reset_session.

5.2 O Flash

O flash é uma parte especial da sessão, que é limpo a cada requisição. Isso significa que os valores armazenados nele estarão disponíveis somente para a próxima requisição, sendo úteis para enviar mensagens de erro, etc.

O flash é acessado através do método flash. Assim como a sessão, o flash é representado por um objeto hash.

Usaremos o evento de logout do usuário como exemplo. O controller pode enviar uma mensagem que será exibida na próxima requisição:

class LoginsController < ApplicationController
  def destroy
    session.delete(:current_user_id)
    flash[:notice] = "Você foi deslogado com sucesso."
    redirect_to root_url, status: :see_other
  end
end

Observe que também é possível definir uma mensagem flash como parte do redirecionamento. Você pode usar :notice, :alert ou o mais genérico, :flash:

redirect_to root_url, notice: "Você foi deslogado com sucesso."
redirect_to root_url, alert: "Você está preso aqui!"
redirect_to root_url, flash: { referral_code: 1234 }

A action destroy redireciona para a root_url da aplicação, onde a mensagem será exibida. Note que é responsabilidade da próxima action decidir o que (e até mesmo se algo) será feito com o valor anterior contido no flash. É comum exibir quaisquer erros, alertas ou avisos vindos do flash no layout da aplicação:

<html>
  <!-- <head/> -->
  <body>
    <% flash.each do |name, msg| -%>
      <%= content_tag :div, msg, class: name %>
    <% end -%>

    <!-- resto do conteúdo -->
  </body>
</html>

Dessa forma, se uma action gerar um aviso, alerta ou mensagem, o layout o exibirá automaticamente.

É possível armazenar qualquer valor aceito pela session no flash, não ficando limitado somente aos avisos, alertas e mensagens:

<% if flash[:just_signed_up] %>
  <p class="welcome">Welcome to our site!</p>
<% end %>

Se quiser que um flash seja acessado em uma outra requisição, use flash.keep:

class MainController < ApplicationController
  # Digamos que essa action corresponde à root_url, mas você quer que
  # todas as requisições sejam redirecionadas para UsersController#index.
  # Se uma action definir o flash e for redirecionada para cá, os valores
  # seriam perdidos quando um ocorrer um outro redirecionamento.
  # Para persistir os valores e evitar que isso ocorra, utilize 'keep'.
  def index
    # Persistirá os valores.
    flash.keep

    # Usando uma chave é possível persistir somente um tipo de valor:
    # flash.keep(:notice)
    redirect_to users_url
  end
end
5.2.1 flash.now

Por padrão, adicionar valores ao flash os tornará disponíveis somente na próxima requisição, mas em alguns casos, você pode querer acessar estes valores em uma mesma requisição. Por exemplo, se a action create falhar ao salvar um recurso e você renderizar o template new diretamente, isso não resultará em uma nova requisição, mas você ainda pode querer exibir a mensagem usando o flash. Para isso, é possível usar o flash.now, de maneira similar ao flash simples:

class ClientsController < ApplicationController
  def create
    @client = Client.new(client_params)
    if @client.save
      # ...
    else
      flash.now[:error] = "Não foi possível salvar o cliente."
      render action: "new"
    end
  end
end

6 Cookies

Sua Aplicação pode armazemar pequenas quantidades de dados no cliente - chamados de cookies - que serão mantidas entre requisições e até as sessões. O Rails fornece um fácil acesso para os cookies através do método cookies, que - assim como a session - funciona como um hash:

class CommentsController < ApplicationController
  def new
    # Preencher automaticamente o nome de quem comentou se ele estiver armazenado em um cookie
    @comment = Comment.new(author: cookies[:commenter_name])
  end

  def create
    @comment = Comment.new(comment_params)
    if @comment.save
      flash[:notice] = "Thanks for your comment!"
      if params[:remember_name]
        # Lembrar o nome de quem fez o comentário
        cookies[:commenter_name] = @comment.author
      else
        # Deletar o cookie do nome de quem fez o comentário, caso exista.
        cookies.delete(:commenter_name)
      end
      redirect_to @comment.article
    else
      render action: "new"
    end
  end
end

Perceba que enquanto para valores de sessão você pode definir a chave como nil, para deletar um valor de cookie você deve usar cookies.delete(:key).

O Rails também fornece um cookie jar assinado e um cookie jar criptografado para amazenar dados sensíveis. O cookie jar assinado anexa uma assinaura criptográfica nos valores do cookie para proteger sua integridade. O cookie jar criptografado, criptografa os valores além de assiná-los, para que eles não possam ser lidos pelo usuário final. Consulte a documentação da API (em inglês) para mais detalhes.

Esses cookie jars especiais usam um serializer para serializar os valores atribuídos em strings e desserializa-os em objetos Ruby na leitura.

Você pode especificar qual serializer usar:

Rails.application.config.action_dispatch.cookies_serializer = :json

O serializer padrão para novas aplicações é :json. Para compatibilidade com aplicações antigas que usam cookies, o :marshal é usado quando a opção serializer não está especificada.

Você também pode definir esta opção como :hybrid, nesse caso o Rails desserializaria de forma transparente os cookies (serializados no formato Marshal) existentes ao ler e reescrevê-los no formaro JSON. Isso é útil para migrar aplicações existentes para o serializer :json.

Também é possível passar um serializer personalizado que responda a load e dump:

Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer

Ao usar o serializer :json ou :hybrid, lembre-se de que nem todos os os objetos Ruby podem ser serializados como JSON. Por exemplo, objetos Date eTime serão serializados como strings, e os Hashes terão suas chaves transformadas em string também.

class CookiesController < ApplicationController
  def set_cookie
    cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
    redirect_to action: 'read_cookie'
  end

  def read_cookie
    cookies.encrypted[:expiration_date] # => "2014-03-20"
  end
end

É aconselhável que você armazene apenas dados simples (strings e números) nos cookies. Se você precisar armazenar objetos complexos, precisará lidar com a conversão manualmente ao ler os valores em requisições subsequentes.

Se você usar o cookie de armazenamento de sessão, isso também se aplicaria aos hashes session e flash.

7 Renderizando Dados XML e JSON

O ActionController faz com que renderizar dados XML ou JSON seja extremamente fácil. Se você gerou um controller usando o scaffold, será algo mais ou menos assim:

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render xml: @users }
      format.json { render json: @users }
    end
  end
end

Você pode observar que no código acima estamos usando render xml: @users, e não render xml: @users.to_xml. Se o objeto não é uma String, então o Rails automaticamente chama to_xml por nós.

8 Filtros

Filtros são métodos que rodam "before" (antes de), "after" (depois de) ou "around" (em torno de) de uma ação de controller.

Filtros são herdados, então se você configurou um filtro em ApplicationController, o mesmo irá rodar em cada controller da sua aplicação.

Filtros "before" são registrados através do método before_action. Eles podem interromper o ciclo de uma requisição. Um filtro comum para "before" é o que requer que um usuário está logado para que uma ação seja executada. Você pode definir o método do filtro dessa forma:

class ApplicationController < ActionController::Base
  before_action :require_login

  private
    def require_login
        unless logged_in?
        flash[:error] = "You must be logged in to access this section"
        redirect_to new_login_url # interrompe o ciclo da requisição
        end
    end
end

Esse método simplesmente armazena uma mensagem de erro no flash e redireciona para o formulário de login se o usuário não estiver logado. Se um filtro "before" renderiza ou redireciona, a ação não será executada. Se filtros adicionais estão programados para executar após esse filtro, eles são cancelados também.

Nesse exemplo, o filtro é adicionado ao ApplicationController e dessa forma todos os controllers na aplicação irão herdar ele. Isso fará com que tudo na aplicação requeira que o usuário esteja logado para que ele possa usar. Por razões óbvias (o usuário não conseguiria fazer o log in para começo de conversa!), nem todos os controllers devem requerer isso. Você pode evitar esse filtro de ser executado antes de ações em particular com skip_before_action:

class LoginsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]
end

Agora, as ações de new e create do LoginsController irão funcionar como antes sem requerer que o usuário esteja logado. A opção :only é usada para pular esse filtro somente para essas ações, e existe também a opção :except que funciona de maneira contrária. Essas opções podem ser utilizadas quando adicionamos filtros também, para que você possa adicionar um filtro que somente executa para as ações selecionadas.

Chamar o mesmo filtro múltiplas vezes com diferentes opções não irá funcionar, já que a última definição do filtro irá sobreescrever as anteriores.

8.1 Filtros after e around

Além de filtros "before", você pode também executar filtros depois que uma ação tenha sido executada, ou antes e depois em conjunto.

Filtros "after" são registrados através do método after_action. Eles são similares aos filtros "before", mas porque a ação já foi executada eles tem acesso a dados da resposta que serão enviados para o cliente. Obviamente, filtros "after" não podem impedir uma ação de ser executada. Note também que filtros "after" são executados somente após uma ação bem sucedida, mas não quando uma exceção é gerada durante o ciclo de uma requisição.

Filtros "around" são registrados através do método around_action. Eles são responsáveis por executar as ações associadas por yield, simular a como os middlewares do Rack funcionam.

Por exemplo, em um website aonde alterações possuem um fluxo de aprovação, um administrador pode pré-visualizar as mesmas facilmente, aplicando-as dentro de uma transação.

class ChangesController < ApplicationController
  around_action :wrap_in_transaction, only: :show

  private
    def wrap_in_transaction
        ActiveRecord::Base.transaction do
        begin
            yield
        ensure
            raise ActiveRecord::Rollback
        end
        end
    end
end

Note que um filtro "around" também envolve a renderização. Em particular, no exemplo acima, se a view efetuar uma leitura no banco de dados (p. ex. usando um scope), a mesma é efetuada dentro de uma transação e então apresenta a informação para visualização.

Você pode escolher não efetuar yield e montar a resposta você mesmo, o que faria com que a ação não fosse executada.

8.2 Outras Formas de Usar Filtros

Enquanto a forma mais comum de se utilizar filtros é criando métodos privados e usando before_action, after_action, ou around_action para adicioná-los, existem duas outras formas para fazer a mesma coisa.

A primeira é utilizar um bloco diretamente com método *_action. O bloco recebe o controller como um argumento. O filtro require_login acima pode ser reescrito para utilizar um bloco:

class ApplicationController < ActionController::Base
  before_action do |controller|
    unless controller.send(:logged_in?)
      flash[:error] = "Você deve estar logado para acessar essa seção"
      redirect_to new_login_url
    end
  end
end

Note que, nesse caso, o filtro utiliza send porque o método logged_in? é privado e o filtro não é executado no escopo do controller. Essa não é a forma recomendada para implementar esse filtro em particular, mas em casos mais simples, ele pode ser útil.

Especificamente para around_action, o bloco também acessa a action:

around_action { |_controller, action| time(&action) }

A segunda forma é utilizar uma classe (na verdade, qualquer objeto que responda aos métodos corretos serve) para gerenciar a filtragem. Isto é útil em casos mais complexos que não são possíveis de serem implementados de uma forma de fácil leitura e reutilizados usando as outras duas abordagens. Por exemplo, você pode reescrever o filtro de login novamente utilizando uma classe:

class ApplicationController < ActionController::Base
  before_action LoginFilter
end

class LoginFilter
  def self.before(controller)
    unless controller.send(:logged_in?)
      controller.flash[:error] = "Você deve estar logado para acessar essa seção"
      controller.redirect_to controller.new_login_url
    end
  end
end

Novamente, esse não é um exemplo ideal para esse filtro, pois não é executado dentro do escopo do controller mas recebe o mesmo como um argumento. A classe de filtro deve implementar um método com o mesmo nome do filtro, então para o filtro de before_action a classe deve implementar um método before, e assim em diante. O método around deve efetuar yield para executar a ação.

9 Proteção de falsificação de requisição

Falsificação de requisições cross-site é um tipo de ataque no qual o site engana o usuário a fim de que ele faça requisições em outro site, possivelmente adicionando, alterando ou deletando informações naquela site sem o conhecimento ou a permissão do usuário.

O primeiro passo para evitar isso é ter certeza que todas as ações "destrutivas" (criar, atualizar, e destruir) possam ser acessadas somente via requisições que não sejam GET. Se você está seguindo as convenções RESTful você já está fazendo isso. Contudo, sites maliciosos continuam podendo enviar requisições não GET para o seu site facilmente, e é aí que a proteção de falsificação de requisição entra. Como o nome diz, ela te protege de requisições falsas.

A forma como isso é feito é adicionando um token não adivinhável que é conhecido apenas pelo seu servidor para cada requisição. Desta forma, se uma requisição chega sem um token conhecido, o seu acesso será negado.

Se você gera um form como este:

<%= form_with model: @user do |form| %>
  <%= form.text_field :username %>
  <%= form.text_field :password %>
<% end %>

Você perceberá como o token é adicionado como um campo invisível.

<form accept-charset="UTF-8" action="/users/1" method="post">
<input type="hidden"
       value="67250ab105eb5ad10851c00a5621854a23af5489"
       name="authenticity_token"/>
<!-- fields -->
</form>

O Rails adiciona esse token para cada form que é gerado usando o form helpers, então na maior parte das vezes você não precisa se preocupar com isso. Se você está escrevendo um form manualmente ou precisa adicionar o token para outra sessão, ele está disponível por meio do método form_authenticity_token.

O form_authenticity_token gera um token de autenticação válido. Isso é útil em lugar aonde o Rails não adiciona o mesmo automaticamente, como em chamadas Ajax personalizadas.

O Guia de segurança possui mais informações sobre isso e muitos outros problemas relacionados a segurança que você deve estar ciente quando desenvolve uma aplicação web.

10 Os Objetos de Requisição e Resposta

Em todo controller, existem dois métodos de acesso apontando para os objetos de requisição e de resposta associados com o ciclo de requisição que estiver em execução no momento. O método request contém uma instância de ActionDispatch::Request e o método response retorna um objeto de resposta representando o que será enviado de volta ao cliente.

10.1 O Objeto request

O objeto de requisição contém muitas informações úteis sobre a requisição proveniente do cliente. Para obter uma lista completa dos métodos disponíveis verifique a documentação da API do Rails e a documentação do Rack. Entre as propriedades que você pode acessar estão:

Propriedade de request Propósito
host O hostname utilizado para esta requisição.
domain(n=2) Os primeiros n segmentos do hostname, iniciando pela direita (o domínio de primeiro nível).
format O tipo de conteúdo requisitado pelo cliente.
method O método HTTP utilizado para a requisição.
get?, post?, patch?, put?, delete?, head? Retorna true se o método HTTP é GET/POST/PATCH/PUT/DELETE/HEAD.
headers Retorna um hash contendo os headers associados com a requisição.
port O número (integer) da porta utilizada para a requisição.
protocol Retorna uma string contendo o protocolo utilizado, além do trecho "://". Por exemplo: "http://".
query_string A query string da URL (todo o trecho após "?").
remote_ip O endereço IP do cliente.
url A URL completa utilizada para a requisição.
10.1.1 path_parameters, query_parameters, e request_parameters

O Rails armazena todos os parâmetros enviados com a requisição no hash params, não importando se eles foram enviados como parte da query string ou no corpo da requisição. O objeto de requisição tem três métodos de acesso que te fornecem acesso a estes parâmetros dependendo de onde eles vieram. O hash query_parameters contem os parâmetros que foram enviados por meio da query_string enquanto o hash request_parameters contém os parâmetros enviados através do corpo da requisição. O hash path_parameters contém os parâmetros que foram reconhecidos pelo roteamento como parte do caminho que leva ao controller e action sendo executados.

10.2 O Objeto response

O objeto de resposta geralmente não é usado diretamente, mas é construído durante a execução da action e renderização dos dados que serão enviados de volta ao usuário, porém às vezes - como num filtro posterior - ele pode ser útil para acessar a resposta diretamente. Alguns destes métodos de acesso também possuem setters, lhe permitindo mudar seus valores. Para obter uma lista completa dos métodos disponíveis verifique a documentação da API do Rails e a documentação do Rack;

Propriedade de response Propósito
body Esta é a string de dados sendo enviada de volta ao usuário. Na maioria dos casos se trata de código HTML.
status O código de status HTTP para a resposta, como um código 200 para uma requisição bem sucedida ou 404 para um arquivo não encontrado.
location A URL que o cliente estiver sendo redirecionado para, caso haja alguma.
content_type O tipo de conteúdo da resposta.
charset O conjunto de caracteres sendo utilizado na resposta. O valor padrão é "utf-8".
headers Headers utilizados para a resposta.
10.2.1 Definindo Headers customizados

Se você quer definir headers customizados para uma resposta então response.headers é o local indicado para ajustar isto. O atributo headers é um hash que mapeia os nomes dos headers para seus respectivos valores, e o Rails irá definir alguns deles automaticamente. Se você quiser adicionar ou modificar um header, basta sinalizá-lo para response.headers da seguinte maneira:

response.headers["Content-Type"] = "application/pdf"

No caso acima faria mais sentido utilizar o setter content_type diretamente.

11 Autenticações HTTP

O Rails vem com três mecanismos de autenticação HTTP embutidos:

  • Autenticação Basic
  • Autenticação Digest
  • Autenticação Token

11.1 Autenticação HTTP Basic

Autenticação HTTP basic é um esquema de autenticação que é suportado pela maioria dos navegadores e outros clientes HTTP. Como um exemplo, considere uma página de administração que será acessável apenas informando um nome de usuário e uma senha na janela de autenticação HTTP basic do navegador. Usar a autenticação embutida é bem fácil e apenas requer que você use um método, http_basic_authenticate_with.

class AdminsController < ApplicationController
  http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end

Com isso, você pode criar controllers com namespaces que herdam de AdminsController. O filtro vai, assim, ser executado para todas as ações nos controllers, protegendo-os com a autenticação HTTP basic.

11.2 Autenticação HTTP Digest

A autenticação HTTP digest é superior à autenticação basic porque ela não requer que o cliente envie uma senha sem criptografia pela rede (embora a autenticação HTTP basic seja segura via HTTPS). Usar a autenticação digest com Rails é bem fácil e requer apenas o uso de um método, authenticate_or_request_with_http_digest.

class AdminsController < ApplicationController
  USERS = { "lifo" => "world" }

  before_action :authenticate

  private
    def authenticate
      authenticate_or_request_with_http_digest do |username|
        USERS[username]
      end
    end
end

Como visto no exemplo acima, o bloco authenticate_or_request_with_http_digest recebe apenas um argumento - o nome de usuário. E o bloco retorna a senha. Retornar false ou nil em authenticate_or_request_with_http_digest causará falha de autenticação.

11.3 Autenticação HTTP Token

A autenticação de token HTTP é um esquema para habilitar o uso de tokens Bearer no header HTTP Authorization. Existem muitos formatos de token disponíveis e sua descrição está fora do escopo desta documentação.

Por exemplo, suponha que você queira usar um token de autenticação emitido com antecedência para realizar a autenticação e o acesso. Implementar autenticação via token com Rails é bastante fácil e requer apenas o uso de um método, authenticate_or_request_with_http_token.

class PostsController < ApplicationController
  TOKEN = "secret"

  before_action :authenticate

  private
    def authenticate
      authenticate_or_request_with_http_token do |token, options|
        ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
      end
    end
end

Como visto no exemplo acima, o bloco authenticate_or_request_with_http_token recebe dois argumentos: o token e umHash contendo as opções que foram analisadas a partir do header HTTP Authorization. O bloco deve retornar true se a autenticação for bem-sucedida. Retornar false ounil causará uma falha de autenticação.

12 Streaming e Downloads de Arquivos

Às vezes você pode querer enviar um arquivo para o usuário ao invés de uma página HTML. Todos os controllers no Rails possuem os métodos send_data e send_file, que transmitem dados ao navegador. O método send_file permite que você proveja o nome do arquivo no disco e seu conteúdo será transmitido.

Para transmitir dados para o navegador, use send_data:

require "prawn"
class ClientsController < ApplicationController
  # Gera um documento PDF com informações no navegador e
  # o retorna. O usuário receberá o documento PDF como um download.
  def download_pdf
    client = Client.find(params[:id])
    send_data generate_pdf(client),
              filename: "#{client.name}.pdf",
              type: "application/pdf"
  end

  private
    def generate_pdf(client)
      Prawn::Document.new do
        text client.name, align: :center
        text "Address: #{client.address}"
        text "Email: #{client.email}"
      end.render
    end
end

A action download_pdf no exemplo acima está chamando um método privado que na verdade cria o documento PDF e o retorna como uma string. Essa string será então transmitida ao navegador como um arquivo para download e o nome do arquivo será sugerido ao usuário. Às vezes, quando arquivos são transmitidos aos usuários, você pode não querer que façam o download do arquivo. Imagens, por exemplo, podem ser embutidas em páginas HTML. Para comunicar ao navegador que não deve ser feito o download do arquivo, você pode utilizar a propriedade inline na opção :disposition. A propriedade oposta e padrão para essa opção é "attachment".

12.1 Enviando Arquivos

Se você deseja enviar um arquivo que já existe no disco, utilize o método send_file.

class ClientsController < ApplicationController
  # Transmite um arquivo que já foi gerado e salvo no disco.
  def download_pdf
    client = Client.find(params[:id])
    send_file("#{Rails.root}/files/clients/#{client.id}.pdf",
              filename: "#{client.name}.pdf",
              type: "application/pdf")
  end
end

Esse método irá ler e transmitir o arquivo 4 kb por vez, evitando carregar o arquivo inteiro em memória de uma vez. Você pode desativar a transmissão com a opção :stream ou ajustar o tamanho do bloco com a opção :buffer_size.

Se a opção :type não for especificada, será presumido de acordo com a extensão especificada no :filename. Se o tipo do conteúdo (content-type) não estiver registrado para a extensão, será usado application/octet-stream.

Tenha cuidado ao utilizar dados vindos do navegador ( params, cookies, etc.) para localizar um arquivo no disco, pois é um risco de segurança que pode permitir a alguém ter acesso a arquivos não permitidos.

Não é recomendado que você transmita arquivos estáticos através do Rails, você pode, ao invés disso, mantê-los em uma pasta pública no seu servidor web. É muito mais eficiente deixar os usuários baixarem os arquivos diretamente utilizando Apache ou outro servidor web, evitando que a requisição passe, sem necessidade, por todo o fluxo do Rails.

12.2 RESTful Downloads

Apesar de o método send_data funcionar tranquilamente, se você está criando uma aplicação RESTful, geralmente não é necessário ter actions separadas para o download de arquivos. Na terminologia REST, o arquivo PDF do exemplo acima pode ser considerado apenas uma outra representação do recurso do navegador. Rails provê um jeito fácil e prático de fazer "downloads RESTful". Veja como você pode re-escrever o exemplo para que o download do PDF seja parte da action show, sem qualquer transmissão:

class ClientsController < ApplicationController
  # O usuário pode solicitar receber este recurso como HTML ou PDF.
  def show
    @client = Client.find(params[:id])

    respond_to do |format|
      format.html
      format.pdf { render pdf: generate_pdf(@client) }
    end
  end
end

Para que este exemplo funcione, você deve adicionar o MIME type ao Rails. Isso pode ser feito adicionando a seguinte linha ao arquivo config/initializers/mime_types.rb:

Mime::Type.register "application/pdf", :pdf

Arquivos de configuração não são recarregados a cada requisição, então você precisa reiniciar seu servidor para que as mudanças tenham efeito.

Agora os usuários podem requerer um arquivo em PDF só adicionando ".pdf" ao final da URL:

GET /clients/1.pdf

12.3 Transmissão Ao Vivo de Dado Arbitrários

O Rails permite que você transmita mais do que apenas arquivos. Na verdade, você pode transmitir o que você desejar como um objeto de resposta. O modulo ActionController::Live permite a você criar uma conexão persistente com o navegador. Utilizando esse módulo, você é capaz de enviar dados arbitrários ao navegador sem depender de uma requisição HTTP.

12.3.1 Implementando Transmissão Ao Vivo ( Live Streaming )

Incluindo ActionController::Live dentro do seu controller irá prover a todas as actions do seu controller a habilidade de transmitir dados. Você pode mesclar o modulo da seguinte forma:

class MyController < ActionController::Base
  include ActionController::Live

  def stream
    response.headers['Content-Type'] = 'text/event-stream'
    100.times {
      response.stream.write "hello world\n"
      sleep 1
    }
  ensure
    response.stream.close
  end
end

O código acima manterá uma conexão constante com o navegador e mandará 100 mensagens de "hello world\n", cada uma com um segundo de diferença.

Existem algumas coisas a serem notadas no exemplo acima. Nós precisamos ter certeza de que a resposta da transmissão foi terminada. Esquecer de encerrar a transmissão deixará o socket aberto pra sempre. Nós também precisamos estabelecer o tipo de conteúdo (content_type) para text/event-stream antes de responder a transmissão. Isso é necessário, pois headers não podem ser escritos depois que uma resposta foi enviada (quando response.committed? retorna um valor truthy), que ocorre quando você escreve ou envia (commit) a resposta de uma transmissão.

12.3.2 Exemplo de Uso

Vamos supor que você estivesse criando uma máquina de Karaokê e um usuário quer achar a letra de uma música em particular. Cada música (Song) tem um número específico de linhas e cada linha tem um tempo (num_beats) para terminar de ser cantada.

Se nós quiséssemos retornar as letras no estilo de Karaokê (mandar a linha só quando a linha anterior for terminada de cantar), então poderíamos usar ActionController::Live da seguinte forma:

class LyricsController < ActionController::Base
  include ActionController::Live

  def show
    response.headers['Content-Type'] = 'text/event-stream'
    song = Song.find(params[:id])

    song.each do |line|
      response.stream.write line.lyrics
      sleep line.num_beats
    end
  ensure
    response.stream.close
  end
end

O código acima envia a próxima linha apenas depois que a pessoa cantando completou a linha anterior.

12.3.3 Considerações da Transmissão

Transmitir dados arbitrários é uma ferramenta extremamente poderosa. Como mostrado nos exemplos anteriores, você pode escolher quando e o que enviar na resposta da transmissão. Entretanto, você deveria se atentar aos seguintes pontos:

  • Cada transmissão cria uma nova thread e copia sobre as variáveis locais da thread, as variáveis da thread original. Ter muitas variáveis locais em uma thread local pode impactar negativamente na performance. Da mesma forma, um grande número de threads pode também piorar a performance.
  • Deixar de encerrar a transmissão deixará o socket correspondente aberto para sempre. Certifique-se de chamar o método close sempre que estiver transmitindo dados.
  • Os servidores WEBrick armazena todas as respostas, então apenas incluir no controller ActionController::Live não irá funcionar. Você deve usar um servidor web que não armazene automaticamente as respostas.

13 Filtragem de Log

Rails mantém um arquivo de log pra cada ambiente na pasta log. Eles são bastante úteis quando estamos depurando o que está de fato acontecendo na sua aplicação, porém você pode não querer que sejam salvos todas as informações sejam armazenadas no arquivo de log em uma aplicação ativa.

13.1 Filtrando Parâmetros

Você pode evitar que parâmetros sensíveis da requisição sejam salvos no seu arquivo de log adicionando-os a config.filter_parameters na configuração da aplicação. Esses parâmetros aparecerão como [FILTERED] no arquivo de log.

config.filter_parameters << :password

Os parâmetros fornecidos serão filtrados correspondendo parcialmente a uma expressão regular. O Rails por padrão adiciona :passw, :secret, :token no initializer apropriado (initializers/filter_parameter_logging.) e se preocupa com parâmetros típicos da aplicação como password, password_confirmation e my_token.

13.2 Filtrando Redirecionamentos

Às vezes é desejável que sejam filtrados dos arquivos de log alguns locais sensíveis para os quais sua aplicação está redirecionando. Você pode fazer isso utilizando uma opção de configuração config.filter_redirect:

config.filter_redirect << 's3.amazonaws.com'

Você pode utilizar uma String, uma expressão regular ou um array com ambos.

config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]

URLs correspondentes com a expressão regular serão marcadas como '[FILTERED]'.

14 Rescue

Muito provavelmente sua aplicação irá conter bugs ou enviar exceções que precisam ser tratadas. Por exemplo, se o usuário acessar um link que não possui uma fonte no banco de dados, o Active Record enviará ActiveRecord::RecordNotFound como exceção.

A exceção padrão do Rails apresenta a mensagem "500 Server Error" para todas as exceções. Se a requisição for feita localmente, um belo traceback e outras informações serão mostradas assim você pode verificar o que deu errado e tratar o problema. Se a requisição for remota o Rails apenas apresentará a mensagem "500 Server Error" para o usuário, ou um "404 Not Found" se houver um erro na rota ou o registro não puder ser encontrado. As vezes você pode querer customizar como esses erros são encontrados e como são apresentados ao usuário. Há diversos níveis de tratamento de excessões disponiveis em uma aplicação Rails:

14.1 Os Templates 404 e 500 Padrão

Por padrão no ambiente de produção a aplicação irá renderizar uma mensagem em um template de erro 404 ou 500. No ambiente de desenvolvimento todas as mensagens de erro são disparadas. Essas mensagens são armazenadas em templates estáticos de HTML na pasta public, em 404.html e 500.html respectivamente. Você pode customizar essas páginas e adicionar algumas estilizações, mas lembre-se elas são HTML estáticos; i.e. você não pode usar ERB, SCSS, CoffeeScript, ou layouts para elas.

14.2 rescue_from

Se você quiser fazer algo mais elaborado quando estiver lidando com erros, você pode usar rescue_from, que trata as exceções de um certo tipo (ou de vários tipos) em um controller inteiro e nas subclasses.

Quando uma exceção acontece e é pega por uma diretiva rescue_from, o objeto da exceção é passado ao handler. O handler pode ser um método ou um objeto Proc passado com a opção :with. Você também pode usar um bloco diretamente ao invés de um objeto Proc.

Aqui está um exemplo de como você pode usar rescue_from para interceptar todos os erros ActiveRecord::RecordNotFound e fazer algo com eles.

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

  private
    def record_not_found
      render plain: "404 Not Found", status: 404
    end
end

É claro, que este exemplo não é nada elaborado e não melhora muito a forma original de lidar com os erros, mas uma vez que você capture todas essas exceções você é livre para fazer o que quiser com elas. Por exemplo, você pode criar uma exceção personalizada para quando o usuário não tem acesso a uma parte da aplicação:

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized, with: :user_not_authorized

  private
    def user_not_authorized
      flash[:error] = "You don't have access to this section."
      redirect_back(fallback_location: root_path)
    end
end

class ClientsController < ApplicationController
  # Check that the user has the right authorization to access clients.
  before_action :check_authorization

  # Note how the actions don't have to worry about all the auth stuff.
  def edit
    @client = Client.find(params[:id])
  end

  private
    # If the user is not authorized, just throw the exception.
    def check_authorization
      raise User::NotAuthorized unless current_user.admin?
    end
end

Ao usar rescue_from com Exception ou StandardError pode causar efeitos colaterais já que previne o Rails de lidar com as exceções apropriadamente. Dessa forma, não é recomendado fazer sem uma boa razão.

Quando rodando em ambiente de desenvolvimento, todos os erros ActiveRecord::RecordNotFound renderizam uma página 404. A não ser que você precise de uma forma especifica de tratar isso você não precisa tratar isso.

Certas exceções são tratadas apenas pela classe ApplicationController, já que são acionadas antes do controller ser iniciado a exceção é executada.

15 Forçar o Protocolo HTTPS

Se você quiser garantir que a comunicação com seu controller seja possível apenas via HTTPS, você deve fazer isso ativando o middleware ActionDispatch::SSL via config.force_ssl na configuração do seu ambiente.

Feedback

Você é incentivado a ajudar a melhorar a qualidade deste guia.

Por favor, contribua caso veja quaisquer erros, inclusive erros de digitação. Para começar, você pode ler nossa sessão de contribuindo com a documentação.

Você também pode encontrar conteúdo incompleto ou coisas que não estão atualizadas. Por favor, adicione qualquer documentação em falta na main do Rails. Certifique-se de checar o Edge Guides (en-US) primeiro para verificar se o problema já foi resolvido ou não no branch main. Verifique as Diretrizes do Guia Ruby on Rails para estilo e convenções.

Se, por qualquer motivo, você encontrar algo para consertar, mas não conseguir consertá-lo, por favor abra uma issue no nosso Guia.

E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem vinda na forum oficial do Ruby on Rails e nas issues do Guia em português.


dark theme icon