Mais em rubyonrails.pro.br: Geral | Download | Deploy | Código | Apresentações | Documentação | Ecossistema | Comunidade | Podcasts | Blogs

Visão Geral do Action Controller

Neste guia você aprenderá como os controllers funcionam e como eles se encaixam no ciclo de uma requisição em sua aplicação. Após ler este guia, você será capaz de:

1 O que faz um Controller?

Action Controller é o C de MVC. Após o roteamento determinar qual controller será utilizado para uma requisição, o controller fica responsável por dar sentido a requisição e produzir uma saída apropriada. Por sorte, o Action Controller faz a maior parte do trabalho pesado por você e utiliza convenções inteligentes para tornar este processo o mais direto possível.

Para a maioria das aplicações RESTful convencionais, o controller receberá a requisição (isto é invisível para você como desenvolvedor), resgatará ou salvará dados em um model e utilizará a view para criar uma saída HTML. Se o seu controller precisar fazer coisas um pouco diferentes, isto não é problema, esta é apenas a forma mais comum de funcionamento de um controller.

Um controller pode ser pensado como um intermediário entre models e views. Ele torna dados do model disponíveis para que uma view possa apresentá-los ao usuário, e os salva ou atualiza conforme solicitado pelo usuário.

Para mais detalhes sobre o processamento de rotas, veja o Roteamento Rails de Fora para Dentro

2 Métodos e Actions

Um controller é uma classe Ruby que herda de ApplicationController e possui métodos como qualquer outra classe. Quando a sua aplicação recebe uma requisição, o roteamento determinará qual controller e action serão executados. O Rails então cria uma instância do controller e executa o método público que tenha o mesmo nome da action.

class ClientsController < ApplicationController def new end end

Por exemplo, se um usuário ir para /clients/new em sua aplicação para adicionar um novo cliente, o Rails irá criar uma instância de ClientsController e executará o método new. Perceba que o método em branco do exemplo acima funcionaria perfeitamente, porque o Rails por padrão renderiza a view new.html.erb, a não ser que o controller diga o contrário. O método new poderia disponibilizar para a view uma instância da variável @client criando um novo Cliente:

def new @client = Client.new end

O Guia de Layouts e renderização explica isto com mais detalhes.

ApplicationController herda de ActionController::Base, que define vários métodos auxiliares. Este guia cobrirá alguns deles, mas se você estiver curioso para saber o que mais existe, você pode vê-los todos na documentação API ou no próprio código fonte.

Apenas métodos públicos podem ser chamados como actions. Isto é uma boa prática para diminuir a visibilidade dos métodos que não devem ser actions, como métodos auxiliares ou filtros.

3 Parâmetros

Você provavelmente irá querer acessar dados enviados por um usuário ou outros parâmetros nas actions de seu controller. Existem dois tipos de parâmetros possíveis em uma aplicação web. O primeiro são parâmetros enviados como parte da URL, são chamados de parâmetros de requisição (query string parameters). Os parâmetros de requisição são todo o conteúdo após a “?” na URL. O segundo tipo de parâmetro normalmente é referenciado como dados de um POST. Esta informação geralmente vem de um formulário HTML que foi preenchido por um usuário. É chamado de dados de um POST porque eles só podem ser enviados como parte de uma requisição HTTP POST. O Rails não faz qualquer distinção entre parâmetros de requisição de parâmetros de POST, e ambos ficam disponíveis no hash params em seu controller.

class ClientsController < ActionController::Base # Esta action utiliza parâmetros de requisição porque é executada por uma # requisição HTTP GET, mas isto não faz diferença na forma como os parâmetros # são acessados. A URL para esta action seria desta forma para acessar # clientes ativos: /clients?status=activated def index if params[:status] = "activated" @clients = Client.activated else @clients = Client.unativated end end # Esta action utiliza parâmetros POST. Eles geralmente vem de um formulário # HTML submetido pelo usuário. A URL RESTful para esta requisição é # "/clients", e os dados são enviados como parte do corpo da requisição. def create @client = Client.new(params[:client]) if @client.save redirect_to @client else # Esta linha sobrescreve o comportamento padrão de renderização, que seria # renderizar a view "create". render :action => "new" end end end

3.1 Parâmetros em Hash e Array

O hash de parâmetros não é limitado a chaves e valores unidimensionais. Ele pode conter arrays e outros hashes (aninhados). Para enviar um array de valores, concatene “[]” ao nome da chave:

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

A URL deste exemplo será codificada como “/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3” já que [ e ] não são permitidos em URLs. Na maioria das vezes você não precisará preocupar-se com isso porque o navegador cuidará deste detalhe por você, e o Rails irá decodificá-lo quando recebê-lo, mas se você precisar enviar requisições para o servidor manualmente você deverá lembrar-se disto.

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

Para enviar um hash você deve inserir o nome da chave dentro dos colchetes:

<form 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>

O valor de params[:client] quando este formulário for submetido será {"name" => “Acme”, “phone” => “12345”, “address” => {"postcode" => “12345”, “city” => “Carrot City”}}. Repare no hash aninhado em params[:client][:address].

Repare que o hash de parâmetros é na verdade uma instância de HashWithIndifferentAccess do Active Support, que é uma subclasse de Hash que permite a você utilizar símbolos e strings como chaves.

3.2 Parâmetros de Roteamento

O hash params sempre conterá as chaves :controller e :action, mas você deve utilizar os métodos controller_name e action_name ao invés de acessar estes valores. Qualquer outro parâmetro definido pelo roteamento, como :id, também estará disponível. Como exemplo, considere uma lista de clientes onde a lista possa mostrar tanto os clientes ativos ou os inativos. Podemos adicionar uma rota que captura o parâmetro :status em uma URL mais legível:

map.connect "/clients/:status", :controller => "clients", :action => "index", :foo => "bar"

Neste caso, quando um usuário abrir a URL /clients/active, params[:status] estará com o valor “active”. Quando esta rota for utilizada, params[:foo] também estará com o valor “bar” exatamente como foi passado na configuração da rota, da mesma forma que params[:action] irá conter “index”.

3.3 default_url_options

Você pode ajustar parâmetros globais padrão que serão utilizados ao gerar URLs com default_url_options. Para fazê-lo, defina um método com este nome em seu controller:

class ApplicationController < ActionController::Base #O parâmetro options é o hash que será passado para +url_for+ def default_url_options(options) {:locale => I18n.locale} end end

Estas opções serão utilizadas como ponto de partida quando gerando, então é possível que eles sejam sobrescritos por url_for. Como este método é definido no controller, você pode defini-lo em ApplicationController e então ele será utilizado para a geração de todas as URLs, ou você pode defini-lo em apenas um controller para as URLs geradas nele.

4 Sessão

Sua aplicação tem uma sessão para cada usuário onde você pode armazenar pequenas quantidades de dados que serão persistidos durante cada requisição. A sessão só fica disponível para o controller e a view e pode utilizar um dos diferentes mecanismos de armazenamento:

  • CookieStore – Armazena tudo no cliente.
  • DRbStore – Armazena os dados dem um servidor DRb.
  • MemCacheStore – Armazena os dados em um memcache.
  • ActiveRecordStore – Armazena os dados em um banco de dados utilizando o Active Record.

Todos os métodos utilizam um cookie – isto é obrigatório e o Rails não permite que qualquer parte da sessão seja passada de outra forma (por exemplo, você não pode utilizar parâmetros de requisição para passar o ID de uma session) por questão de segurança (é mais fácil interceptar e modificar uma sessão quando o ID faz parte da URL).

A maioria utiliza um cookie para armazenar o ID da sessão, que é então utilizado para procurar os dados da sessão no servidor. O método padrão e recomendado, o CookieStore, não armazena dados da sessão no servidor, mas sim no próprio cookie. Os dados são assinados criptograficamente para torná-los a prova de alterações, mas eles não são criptografados, então qualquer um com acesso ao cookie pode ler seu conteúdo, mas não editá-lo (o Rails não irá aceitá-lo se ele tiver sido editado).

O CookieStore só pode armazenar cerca de 4kB de dados – bem menos do que os outros métodos – mas isto geralmente é o suficiente. O armazenamento de grandes quantidades de dados não é recomendado, não importa qual o método de armazenamento de sessão a sua aplicação utilize. Você deve evitar principalmente armazenar objetos complexos (qualquer coisa diferente de objetos Ruby básicos, sendo o exemplo mais comum uma instância de um model) na sessão, já que o servidor pode não conseguir recuperá-los entre requisições, o que resultará em um erro. O armazenamento CookieStore tem a vantagem de não requerer qualquer configuração antes de ser utilizado – o Rails irá gerar uma “chave secreta” que será utilizada para assinar o cookie quando você criar a aplicação.

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

Se você precisar de um mecanismo de armazenamento de sessão diferente, você pode alterá-lo no arquivo config/environment.rb:

# Escolha um dentre [:active_record_store, :drb_store, :mem_cache_store, :cookie_store] config.action_controller.session_store = :active_record_store

Rails define a session key (o nome do cookie) e (para o CookieStore) e uma chave secreta quando assina os dados da session. Isto também pode ser alterado em config/initializers/session_store.rb:

# Sua chave secreta para verificar a integridade dos dados da session no cookie. # Se você alterar esta chave, todas as sessions antigas se tornarão inválidas! # Garanta que ela possua no mínimo 30 caracteres e todos aleatórios. # nenhuma palavra comum ou você estará exposto a ataques de dicionário. ActionController::Base.session = { :key => '_yourappname_session', :secret => '4f50711b8f0f49572...' }

Alterando a secret quando estiver usando CookieStore irá invalidar todas as sessions existentes.

4.1 Acessando a Sessão

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

Sessions são carregadas por demanda (lazy load). Se você não acessar a session no código de sua action, ela não será carregada. Por isso você nunca precisa desativar a session, apenas não acessando-a já fará o serviço.

Valores são armazenados na sessão utilizando pares de chaves/valores como um hash:

class ApplicationController < ActionController::Base private # Encontra o User com o ID armazenado na sessão com a chave :current_user_id # Esta é uma forma comum de lidar com login de usuários em uma aplicação Rails; # Durante o login o valor é armazenado, e no logout ele é removido. def current_user @_current_user ||= session[:current_user_id] && User.find(session[:current_user_id]) end end

Para armazenar algo na sessão, simplesmente atribua uma chave como um hash:

class LoginsController < ApplicationController # "Cria" um login, ou seja, "loga o usuário" def create if user = User.authenticate(params[:username, params[:password]) # Registra o ID do usuário na sessão para que possa ser recuperado durante outras requisições session[:current_user_id] = user.id redirect_to root_url end end end

Para remover algo da sessão, atribua o valor nil para a chave:

class LoginsController < ApplicationController # "Apaga" um login, ou seja, "desloga o usuário" def destroy # Remove o id do usuário da sessão session[:current_user_id] = nil redirect_to root_url end end

Para reiniciar a sessão inteira, utilize reset_session.

4.2 O flash

O flash é uma parte especial da sessão que é esvaziada entre cada requisição. Isto significa que valores armazenados nele só ficarão disponíveis durante a próxima requisição, o que é útil para armazenar mensagens de erro, etc. Ele é acessado igual a sessão, como um hash. Vamos utilizar o ato de logout como exemplo. O controller pode enviar uma mensagem que será apresentada ao usuário na próxima requisição:

class LoginsController < ApplicationController def destroy session[:current_user_id] = nil flash[:notice] = "Você saiu do sistema" redirect_to root_url end end

A action destroy redireciona a aplicação para root_url, onde a mensagem será apresentada. Repare que fica a cargo da próxima action o que fazer com o que a action anterior colocou no flash. É normal apresentar erros ou informações a partir do flash no layout da aplicação:

<html> <!-- <head/> --> <body> <% if flash[:notice] -%> <p class="notice"><%= flash[:notice] %></p> <% end -%> <% if flash[:error] -%> <p class="error"><%= flash[:error] %></p> <% end -%> <!-- more content --> </body> </html>

Desta forma, se uma action atribuir uma mensagem de erro ou de informação no flash, o layout irá apresentá-la automaticamente.

Se você quiser que um valor no flash seja mantido para outra requisição, utilize o método keep:

class MainController < ApplicationController # Esta action corresponde a root_url, mas você deseja # que todas as requisições aqui sejão redirecionadas para UsersController#index. # Se a action definir um flash e ser redirecionada aqui, o valor # normalmente seria perdido quando outro redirecionamento ocorrer, mas você pode # usar 'keep' para manter o valor para outra requisição. def index # Irá persistir os valores no flash flash.keep # Você também pode usar uma chave para persistir apenas um tipo de valor. # flash.keep(:notice) redirect_to users_url end end
4.2.1 flash.now

Por padrão, adicionar valores ao flash irá torná-los disponíveis durante a próxima requisição, mas algumas vezes você pode querer acessar estes valores nesta mesma requisição. Por exemplo, se a action create falha em salvar um recurso e você renderizar o template new diretamente, isto não irá resultar em uma nova requisição, mas você pode ainda querer mostrar uma mensagem utilizando o flash. Para fazê-lo, você pode utilizar flash.now da mesma forma que utiliza o flash:

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

5 Cookies

A sua aplicação pode armazenar pequenas quantidades de dados no cliente — através dos chamados cookies — que persistirá entre requisições e até mesmo entre sessões. O Rails provê fácil acesso a cookies através do método cookies, que — assim como o método session — funciona como um hash:

class CommentsController < ApplicationController def new #Recupera automaticamente o nome do comentador se tiver sido armazenado em um cookie @comment = Comment.new(:name => cookies[:commenter_name]) end def create @comment = Comment.new(params[:comment]) if @comment.save flash[:notice] = "Obrigado pelo seu comentário!" if params[:remember_name] # Memorizar o nome do comentador cookies[:commenter_name] = @comment.name else # Não memorizar, e apagar o nome se tiver sido memorizado antes cookies.delete(:commenter_name) end redirect_to @comment.article else render :action => "new" end end end

Perceba que, enquanto para valores da sessão você atribui nil à chave para removê-la, para apagar o valor de um cookie você deve utilizar cookies.delete(:key).

6 Filtros

Filtros são métodos que são executados antes, depois ou durante uma action de um controller. Por exemplo, um filtro pode verificar se o usuário logado tem permissão para acessar um determinado controller ou action. Filtros são herdados, então se você criar um filtro em ApplicationController, ele será executado em cada controller da sua aplicação. Um filtro simples e comum é um que requer um usuário logado para executar uma action. Você pode definir o método filtro desta forma:

class ApplicationController < ActionController::Base before_filter :require_login private def require_login unless logged_in? flash[:error] = "Você deve estar logado para acessar esta seção" redirect_to new_login_url # Previne que a ação corrente seja executada end end # O método logged_in? simplesmente retorna true se o usuário estiver logado # e false em caso contrário. Ele faz isto "booleanizando" o método current_user # que nós criamos anteriormente utilizando um operador ! duplo. Saiba que isto # não é comum em Ruby e é desencorajado a não ser que você realmente queira converter # algo em true ou false. def logged_in? !!current_user end end

O 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’ (um filtro que é executado antes da ação) renderizar ou redirecionar, a action não será executada. Se houverem filtro adicionais a serem executados após a renderização ou redirecionamento, eles também serão cancelados.

Neste exemplo, o filtro é adicionado ao ApplicationController e consequentemente a todos os controllers da aplicação. Isto fará com que tudo na aplicação exija que o usuário esteja logado para utilizá-lo. Por razões óbvias (o usuário nem conseguiria logar!), nem todos os controllers ou actions devem exigir isto. Você pode evitar que este filtro seja executado antes de algumas actions com skip_before_filter :

class LoginsController < ApplicationController skip_before_filter :require_login, :only => [:new, :create] end

Agora as actions “new” e “create” do LoginsController irão funcionar como antes, sem exigir que o usuário esteja logado. A opção :only é utilizada para pular a execução do filtro para determinadas actions, e também existe a opção :except, que funciona da forma contrária. Estas opções também podem ser utilizadas quando estiver adicionando filtros, assim você pode criar um filtro que só é executado para algumas actions.

6.1 Filtros Posteriores (After Filters) e Filtros Antes e Depois (Around Filters)

Além do filtro anterior (before filter), você também pode executar filtros depois de uma action ou até mesmo um filtro que é executado antes e depois de uma action. O filtro posterior é similar ao filtro anterior, mas, adicionalmente, como a action já foi executada, ele também provê acesso aos dados de resposta que estão para serem enviados ao cliente. Obviamente, filtros posteriores não podem evitar que actions sejam executadas.

Filtros antes e depois (Around Filters) ficam responsáveis por executar uma action, e eles podem escolher não fazê-lo, que é a forma do Around Filter evitar que uma action seja executada.

# Exemplo pego da documentação de filtros da API do Rails: # http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html class ApplicationController < ActionController::Base around_filter :catch_exceptions private def catch_exceptions yield rescue => exception logger.debug "Exceção capturada! #{exception}" raise end end

6.2 Outras Formas de Utilizar Filtros

Enquanto a forma mais comum de utilização dos filtros é criar métodos privados e utilizar os *_filter (before, after, etc), existem outras duas formas de obter o mesmo resultado.

A primeira é utilizar um bloco diretamente com os métodos *_filter. O bloco recebe um controller como argumento, e o filtro require_login mostrado acima poderia ser reescrito da seguinte forma utilizando um bloco:

class ApplicationController < ActionController::Base before_filter { |controller| redirect_to new_login_url unless controller.send(:logged_in?) } end

Perceba que o filtro neste caso utiliza o método send já que o método logged_in? é privado e o filtro não é executado no escopo do controller. Esta não é a forma mais recomendada de implementar este filtro, mas para casos mais simples ela pode ser útil.

A segunda forma é utilizar uma classe (na verdade, qualquer objeto que responda aos métodos corretamente servirão) para lidar com o filtro. Isto é útil para casos mais complexos que não podem ser implementados de forma legível e reutilizável através dos outros dois métodos. Como exemplo, você poderia reescrever o filtro de login utilizando uma classe:

class ApplicationController < ActionController::Base before_filter LoginFilter end class LoginFilter def self.filter(controller) unless logged_in? controller.flash[:error] = "Você deve estar logado para acessar esta seção" controller.redirect_to controller.new_login_url end end end

Lembre-se, este não é o exemplo ideal para este filtro, porque ele não é executado no escopo do controller mas recebe o controller como um argumento. A classe filtro tem um método de classe chamado filter que é executado antes ou depois da action, dependendo dele ser um filtro anterior ou posterior. Classes utilizadas como around filter também podem utilizar este mesmo método filter, que será executado da mesma forma. O método deve realizar um yield para executar a ação. Alternativamente, ela pode ter um método before e um after para serem executados antes e depois da action.

A documentação da API do Rails tem mais informações sobre filtros .

7 Verificações

Verificações servem para garantir que um determinado critério seja verificado para permitir que um controller ou uma action sejam executados. Elas podem especificar que uma determinada chave (ou várias chaves na forma de um array) deva estar presente nos hashes params, session ou flash, ou que um determinado método HTTP seja utilizado, ou que a requisição tenha sido feito utilizando XMLHttpRequest (Ajax). A action padrão realizada quando os critérios não são atendidos é renderizar uma resposta 400 Bad Request, mas você pode personalizar isto especificando um redirecionamento de URL ou renderizando algo específico, e você também pode adicionar mensagens flash e cabeçalhos HTTP a resposta. Na documentação da API este recurso está descrito como “essencialmente um tipo especial de before_filter”.

Aqui temos um exemplo de utilização de verificação para ter certeza de que o visitante passou um usuário e senha para efetuar login:

class LoginsController < ApplicationController verify :params => [:username, :password], :render => {:action => "new"}, :add_flash => {:error => "Usuário e senha são necessários para efetuar login"} def create @user = User.authenticate(params[:username], params[:password]) if @user flash[:notice] = "Você entrou no sistema" redirect_to root_url else render :action => "new" end end end

Agora a action create não será executada a não ser que os parâmetros “username” e “password” estejam presentes, e se eles não estiverem, uma mensagem de erro será adicionada ao flash, e a action “new” será renderizada. Mas existe algo importante faltando na verificação acima: ela será utilizada para qualquer action do LoginsController, o que não queremos. Você pode limitar a quais actions a verificação será utilizada com as opções :only e :except, iguais a um filtro:

class LoginsController < ApplicationController verify :params => [:username, :password], :render => {:action => "new"}, :add_flash => { :error => "Username and password required to log in" } def create @user = User.authenticate(params[:username], params[:password]) if @user flash[:notice] = "You're logged in" redirect_to root_url else render :action => "new" end end end

8 Proteção contra Falsificação de Requisições

Falsificação de requisições entre sites (Cross-site request forgery) é um tipo de ataque onde um site engana um usuário fazendo-ô realizar requisições para outro site, possivelmente adicionando, modificando ou apagando dados no site sem o conhecimento e permissão do usuário. O primeiro passo para evitar isto é certificar-se de que todas as actions “destrutivas” (create, update e destroy) só possam ser acessadas com requisições de um tipo diferente de GET. Se você está seguindo as convenções RESTful você já estará fazendo isso. No entanto, um site malicioso ainda assim pode fazer solicitações de um tipo diferente de GET para o seu site facilmente, e é aí que a proteção contra a falsificação de requisições entra. Como diz o nome, ele protege contra falsificação de requisições. Isto é feito adicionando um valor que não possa ser adivinhado e que só será conhecido pelo seu servidor a cada requisição. Desta forma, se uma requisição chegar sem o valor apropriado, ela terá seu acesso negado.

Se você gerar um formulário desta forma:

<% form_for @user do |f| -%> <%= f.text_field :username %> <%= f.text_field :password -%> <% end -%>

Você perceberá que o valor é adicionado como um campo do tipo hidden:

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

O Rails adiciona este valor para cada formulário que é gerado com os helpers de formulário, então na maior parte dos casos você não precisará se preocupar com isso. Se você escrever um formulário manualmente ou precisar adicionar o valor por qualquer outra razão, ele está disponível através do método form_authenticity_token:

O método form_authenticity_token gera um token de autenticação válido. Isto é útil em casos onde o Rails não o adiciona automaticamente, como chamadas Ajax customizadas.

O Guia de Segurança tem mais informações sobre isto e várias outras informações sobre questões de segurança que você deve estar ciente quando estiver desenvolvendo sua aplicação web.

9 Os objetos request e response

Em cada controller existem dois métodos de acesso que apontam para os objetos request (requisição) e response (resposta) associados com o ciclo da requisição em andamento. O método request contém uma instância de AbstractRequest e o método response retorna um objeto response representando o que será enviado de volta para o cliente.

9.1 O Objeto request

O objeto request contém várias informações úteis sobre a requisição vinda do cliente. Para ver uma lista completa com os métodos disponíveis, verifique a documentação da API. Dentre as propriedades acessíveis estão:

Propriedade do request Propósito
host O hostname usado na requisição.
domain(n=2) Os hostnames começando com n seguementos, iniciando da direita.
format O content type requisitado pelo cliente.
method O método HTTP usado para requisição.
get?, post?, put?, delete?, head? Retorna verdadeiro se o método HTTP é GET/POST/PUT/DELETE/HEAD.
headers Retorna um hash contendo o cabeçalho associado com a requisição.
port O número da porta (integer) usado para requisição.
protocol Retorna uma string contendo o protocólo usado mais “://”, por exemplo “http://”.
query_string A query string da URL, i.e., tudo depois de “?”.
remote_ip O endereço IP do cliente.
url A URL completa usada para a requisição.
9.1.1 path_parameters, query_parameters e request_parameters

O Rails armazena todos os parâmetros enviados com a requisição no hash params, sejam eles sido enviados como parte da string de consulta na URL ou no corpo de um post. O objeto de requisição tem três métodos de acesso que lhe dão acesso a estes parâmetros dependendo da forma como eles vem. O hash query_parameters contém os parâmetros que foram enviados como parte da string de consulta na URL, enquanto o hash request_parameters contém os parâmetros enviados como parte do corpo de um post. O hash path_parameters contém parâmetros que foram reconhecidos pelo roteamento como parte do caminho levando ao controller e a action.

9.2 O Objeto response

O objeto response geralmente não é utilizado diretamente. Ele é construído durante a execução de uma action e renderização dos dados que serão enviados para o usuário, mas algumas vezes – como num filtro posterior – pode ser útil acessar o objeto de resposta diretamente. Alguns dos métodos de acesso também possuem setters, permitindo que você altere seus valores.

Propriedade do response Propósito
body A string de dados que será enviada de volta para o cliente. Geralmente contém código HTML.
status O código de status HTTP para a resposta, como 200 para uma requisição com sucesso ou 404 para arquivo não encontrado.
location A URL que o cliente está sendo redirecionado, se houver redirecionamento.
content_type O tipo de conteúdo da resposta.
charset O conjunto de caracteres sendo utilizado na resposta. O padrão é “utf8”.
headers Os cabeçalhos sendo utilizados na resposta.
9.2.1 Atribuindo Cabeçalhos Personalizados

Se você quiser atribuir cabeçalhos personalizados para um resposta, então response.headers será seu amigo. O atributo de cabeçalhos é um hash que mapeia nomes de cabeçalhos para seus valores, e o Rails irá atribuir alguns deles – como “Content-Type” – automaticamente. Se você quiser adicionar ou alterar um cabeçalho, simplesmente adicione-o ao headers com seu nome e valor:

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

10 Autenticações HTTP

Rails vem com dois mecanismos de autenticação HTTP:

  • Basic Authentication
  • Digest Authentication

10.1 HTTP Basic Authentication

HTTP Basic authentication é a um esquema de autenticação suportado pela maioria dos browsers e outros clientes HTTP. Como exemplo, uma seção administrativa que será apenas disponível para quem fornecer um nome de usuário e senha na tela de autenticação HTTP do browser. Usar o mecanismo padrão é razoavelmente simples e apenas requer o uso do método authenticate_or_request_with_http_basic.

class AdminController < ApplicationController USERNAME, PASSWORD = "humbaba", "5baa61e4" before_filter :authenticate private def authenticate authenticate_or_request_with_http_basic do |username, password| username == USERNAME && Digest::SHA1.hexdigest(password) == PASSWORD end end end

Com isto criado, você pode criar controllers dentro de um namespace que herdem de AdminController. O before filter irá ser executado em todas as actions destes controllers, protegendo-os com HTTP Basic authentication.

10.2 HTTP Digest Authentication

HTTP Digest authentication é superior que Basic authentication já que ele não requer que o client envie a senha desencriptada através da rede. Usar Digest authentication com Rails é relativamente simples e apenas requer o uso de um método, authenticate_or_request_with_http_digest.

class AdminController < ApplicationController USERS = { "lifo" => "world" } before_filter :authenticate private def authenticate authenticate_or_request_with_http_digest do |username| USERS[username] end end end

Como visto acima, o bloco authenticate_or_request_with_http_digest recebe apenas um argumento – username. E o bloco retorna a senha. Retornando false ou nil de authenticate_or_request_with_http_digest irá causar uma falha de autenticação.

11 Streaming e Download de Arquivos

Algumas vezes você pode querer enviar um arquivo para o usuário ao invés de renderizar uma página HTML. Todos os controllers no Rails possuem os métodos send_data e send_file, que podem ser utilizados para enviar dados para o cliente. send_file é um método conveniente que permite que você informe o nome de um arquivo no disco, e ele se encarregará de enviar o conteúdo do arquivo para o cliente.

Para enviar dados para o cliente, utilize send_data:

require "prawn" class ClientsController < ApplicationController # Gera um documento PDF com informações do cliente e o retorna. # O usuário receberá o PDF como um arquivo para download. def download_pdf client = Client.find(params[:id]) send_data(generate_pdf, :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 invocará um método privado que gera o arquivo (um documento PDF) e retorna-o como uma string. Esta string será então enviada para o cliente como um arquivo para ser baixado e um nome para o arquivo será sugerido ao usuário. Algumas vezes quando enviado arquivos para o usuário, você pode não querer que ele baixe o arquivo. Imagens, por exemplo, que podem ser inseridas em páginas HTML. Para dizer ao navegador que um arquivo não é para ser baixado, você pode atribuir à opção :disposition o valor “inline”. O oposto é o valor padrão para esta opção, “attachment”.

11.1 Enviando Arquivos

Se você quiser enviar um arquivo já existente no disco, utilize o método send_file. Isto não é recomendado, mas pode ser útil caso você queira fazer alguma autenticação antes de permitir que o usuário baixe o arquivo.

class ClientsController < ApplicationController # Envia um arquivo já gerado e gravado em disco def download_pdf client = Client.find(params[:id]) send_data("#{RAILS_ROOT}/files/clients/#{client.id}.pdf", :filename => "#{client.name}.pdf", :type => "application/pdf") end end

Isto irá ler e enviar o arquivo 4Kb por vez, evitando carregar o arquivo inteiro na memória de uma só vez. Você pode desligar o streaming com a opção :stream ou ajustar o tamanho dos blocos com a opção :buffer_size.

Tome cuidado quando utilizar (ou simplesmente não utilize) dados “de fora” (parâmetros, cookies, etc) para localizar um arquivo em disco, já que isto é um risco de segurança que pode permitir acesso a arquivos que não deveria.

Não é recomendado fazer stream de arquivos estáticos através do Rails se você pode simplesmente mantê-los em uma pasta pública no seu servidor web. É muito mais eficiente deixar o usuário baixar o arquivo utilizando diretamente o Apache ou outro servidor web, não precisando fazer com que a requisição passe por toda a pilha de controle do Rails.

No entanto se você precisar que a requisição passe pelo Rails por alguma razão, você pode definir a opção :x_sendfile para true, e o Rails irá permitir que o web server cuide do envio do arquivo para o usuário, liberando o processo do Rails para fazer outros trabalhos. Porém o servidor web deve ter suporte ao header X-Sendfile para funcionar, e você precisa continuar tomando cuidado para não aceitar qualquer entrada do usuário que permita que ele tenha acesso a arquivos arbitrários.

11.2 Downloads RESTful

Apesar do método send_data funcionar bem, se você estiver criando uma aplicação RESTful, ter actions separadas para baixar arquivos geralmente não é necessário. Na terminologia REST, o arquivo PDF do exemplo acima pode ser considerado simplesmente outra representação de um recurso para o cliente. O Rails provê uma forma fácil de permitir “downloads RESTful”. Aqui está como você pode reescrever o exemplo para que o download do PDF seja parte da action show, sem qualquer streaming:

class ClientsController < ApplicationController # O usuário pode solicitar este recurso como um 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 este exemplo funcionar, você deve adicionar o MIME type PDF ao Rails. Isto 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ê deverá reiniciar o servidor para que as alterações sejam efetivadas.

Agora o usuário pode requisitar uma versão PDF de um cliente simplesmente adicionando “.pdf” a URL:

GET /clients/1.pdf

12 Filtrando Parâmetros

O Rails mantém um arquivo de log para cada ambiente (desenvolvimento, teste e produção) na pasta log. Eles são extremamente úteis para quando você estiver depurando a sua aplicação, mas em uma aplicação real você pode não querer que cada porção de informação seja armazenada no arquivo de log. O método filter_parameter_logging pode ser utilizado para filtrar informações sensíveis de seu log. Ele funciona substituindo certos valores no hash params por “[FILTERED]” enquanto eles são escritos no log. Como exemplo, vejamos como filtrar todos os parâmetros com chaves que incluam “password”:

class ApplicationController < ActionController::Base filter_parameter_logging :password end

O método funciona recursivamente em todos os níveis do hash de parâmetros e possui um segundo parâmetro opcional que é utilizado como a string de substituição caso esteja presente. Ele também pode receber um bloco que recebe cada chave existente e substitui aquelas que o bloco retornar true.

13 Rescue (Resgatar)

Provavelmente sua aplicação irá conter bugs ou então poderá lançar exceções que precisarão ser tratadas. Por exemplo, se um usuário seguir um link que aponta para um recurso que não existe mais no banco de dados, o Active Record irá lançar uma exceção ActiveRecord::RecordNotFound. O tratamento padrão de exceções do Rails é mostrar uma mensagem de erro no servidor (500 Server Error) para todas as exceções. Se a requisição foi feita localmente, um rastro da execução e algumas informações adicionais são mostradas para facilitar a depuração do erro. Se a requisição foi remota o Rails simplesmente irá mostrar uma mensagem “500 Server Error” para o usuário, ou então uma mensagem “404 Not Found” se houve um erro de roteamento ou um registro não pode ser encontrado. Algumas vezes você pode querer personalizar como estes erros são capturados e como eles serão mostrados ao usuário. Existem alguns níveis de tratamento de exceções disponíveis em um aplicação Rails:

13.1 Os Template Padrões de 500 e 404

Por padrão uma aplicação em modo de produção irá renderizar uma mensagem de erro 404 ou 500. Estas mensagens estão contidas em arquivos HTML estáticos na pasta public, em 404.html e 500.html respectivamente. Você pode personalizar estes arquivos e adicionar alguma informação extra e um layout, mas lembre-se que eles são estáticos, ou seja, você não pode utilizar RHTML ou layout neles, somente HTML puro.

13.2 rescue_from

Se você quiser fazer algo mais elaborado na captura de erros, você pode utilizar o rescue_from, que lida com exceções de um certo tipo (ou vários tipos) em um controller inteiro e suas subclasses. Quando uma exceção ocorre e é capturada pela diretriz rescue_from, o objeto da exceção é passado à quem ficou responsável pela exceção. O responsável pode ser um método ou um objeto Proc passado com a opção :with. Você também pode utilizar um bloco diretamente ao invés de um objeto Proc.

Aqui está como você pode utilizar o rescue_from para interceptar todos os erros do tipo ActiveRecord::RecordNotFound e fazer alguma coisa com eles.

class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found private def record_not_found render :text => "404 Not Found", :status => 404 end end

Claro que este não é um exemplo complexo e não melhora em nada o tratamento padrão de exceções, mas assim que você capturar as exceções você estará livre para fazer o que quiser com elas. Por exemplo, você poderia criar uma classe de exceção personalizada que seria lançada quando o usuário não tivesse acesso a uma determinada seção de sua 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_to :back end end class ClientsController < ApplicationController # Check that the user has the right authorization to access clients. before_filter :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

Algumas exceções só são resgatáveis a partir da classe ApplicationController, já que elas são lançadas antes de o controller ser inicializado e a action seja executada. Veja o artigo de Pratik Naik sobre o assunto para obter mais informações.

14 Changelog

Lighthouse ticket

  • Março 2, 2009: Revisão da tradução por Daniel Lopes
  • Novembro 4, 2008: Primeira versão por Tore Darrell