1 ¿Qué Hace un Controlador?
Action Controller es la C en MVC. Después de que el enrutador ha determinado qué controlador usar para una solicitud, el controlador es responsable de interpretar la solicitud y producir la salida adecuada. Afortunadamente, Action Controller hace la mayor parte del trabajo preliminar por ti y utiliza convenciones inteligentes para hacer esto lo más sencillo posible.
Para la mayoría de las aplicaciones convencionales RESTful, el controlador recibirá la solicitud (esto es invisible para ti como desarrollador), recuperará o guardará datos de un modelo y usará una vista para crear una salida HTML. Si tu controlador necesita hacer las cosas de manera un poco diferente, eso no es un problema, esta es solo la forma más común de que un controlador funcione.
Por lo tanto, se puede pensar en un controlador como un intermediario entre modelos y vistas. Hace que los datos del modelo estén disponibles para la vista, para que pueda mostrar esos datos al usuario, y guarda o actualiza los datos del usuario en el modelo.
NOTA: Para más detalles sobre el proceso de enrutamiento, consulta Enrutamiento de Rails desde el Exterior Hacia Adentro.
2 Convención de Nombres de Controladores
La convención de nomenclatura de controladores en Rails favorece la pluralización de la última palabra en el nombre del controlador, aunque no es estrictamente necesario (por ejemplo, ApplicationController
). Por ejemplo, ClientsController
es preferible a ClientController
, SiteAdminsController
es preferible a SiteAdminController
o SitesAdminsController
, y así sucesivamente.
Seguir esta convención te permitirá usar los generadores de rutas predeterminados (por ejemplo, resources
, etc.) sin necesidad de calificar cada :path
o :controller
, y mantendrá el uso de los ayudantes de rutas nombradas consistente en toda tu aplicación. Consulta Guía de Layouts y Renderizado para más detalles.
NOTA: La convención de nombres de controladores difiere de la convención de nombres de modelos, que se espera que estén nombrados en forma singular.
3 Métodos y Acciones
Un controlador es una clase Ruby que hereda de ApplicationController
y tiene métodos como cualquier otra clase. Cuando tu aplicación recibe una solicitud, el enrutamiento determinará qué controlador y acción ejecutar, luego Rails crea una instancia de ese controlador y ejecuta el método con el mismo nombre que la acción.
class ClientsController < ApplicationController
def new
end
end
Por ejemplo, si un usuario va a /clients/new
en tu aplicación para agregar un nuevo cliente, Rails creará una instancia de ClientsController
y llamará a su método new
. Ten en cuenta que el método vacío del ejemplo anterior funcionaría bien porque Rails, por defecto, renderizará la vista new.html.erb
a menos que la acción indique lo contrario. Al crear un nuevo Client
, el método new
puede hacer que una variable de instancia @client
sea accesible en la vista:
def new
@client = Client.new
end
La Guía de Layouts y Renderizado explica esto con más detalle.
ApplicationController
hereda de ActionController::Base
, que define una serie de métodos útiles. Esta guía cubrirá algunos de estos, pero si tienes curiosidad por ver qué hay allí, puedes ver todos ellos en la documentación de la API o en el propio código fuente.
Solo los métodos públicos son invocables como acciones. Es una buena práctica reducir la visibilidad de los métodos (con private
o protected
) que no están destinados a ser acciones, como métodos auxiliares o filtros.
ADVERTENCIA: Algunos nombres de métodos están reservados por Action Controller. Redefinirlos accidentalmente como acciones, o incluso como métodos auxiliares, podría resultar en un SystemStackError
. Si limitas tus controladores solo a acciones de Enrutamiento de Recursos RESTful, no deberías preocuparte por esto.
NOTA: Si debes usar un método reservado como nombre de acción, una solución es usar una ruta personalizada para mapear el nombre del método reservado a tu método de acción no reservado.
4 Parámetros
Probablemente querrás acceder a los datos enviados por el usuario u otros parámetros en tus acciones de controlador. Hay dos tipos de parámetros posibles en una aplicación web. Los primeros son parámetros que se envían como parte de la URL, llamados parámetros de cadena de consulta. La cadena de consulta es todo lo que está después de "?" en la URL. El segundo tipo de parámetro se refiere generalmente como datos POST. Esta información generalmente proviene de un formulario HTML que ha sido completado por el usuario. Se llama datos POST porque solo se puede enviar como parte de una solicitud HTTP POST. Rails no hace distinción entre los parámetros de cadena de consulta y los parámetros POST, y ambos están disponibles en el hash params
en tu controlador:
class ClientsController < ApplicationController
# Esta acción usa parámetros de cadena de consulta porque se ejecuta
# mediante una solicitud HTTP GET, pero esto no hace ninguna diferencia
# en cómo se acceden los parámetros. La URL para
# esta acción se vería así para listar clientes activados:
# /clients?status=activated
def index
if params[:status] == "activated"
@clients = Client.activated
else
@clients = Client.inactivated
end
end
# Esta acción usa parámetros POST. Lo más probable es que provengan
# de un formulario HTML que el usuario ha enviado. La URL para
# esta solicitud RESTful será "/clients", y los datos se enviarán
# como parte del cuerpo de la solicitud.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
# Esta línea anula el comportamiento de renderizado predeterminado, que
# habría sido renderizar la vista "create".
render "new"
end
end
end
4.1 Parámetros Hash y de Array
El hash params
no está limitado a claves y valores unidimensionales. Puede contener arrays y hashes anidados. Para enviar un array de valores, agrega un par de corchetes vacíos "[]" al nombre de la clave:
GET /clients?ids[]=1&ids[]=2&ids[]=3
NOTA: La URL real en este ejemplo estará codificada como "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5d=3" ya que los caracteres "[" y "]" no están permitidos en las URLs. La mayoría de las veces no tienes que preocuparte por esto porque el navegador lo codificará por ti, y Rails lo decodificará automáticamente, pero si alguna vez te encuentras teniendo que enviar esas solicitudes al servidor manualmente, debes tener esto en mente.
El valor de params[:ids]
ahora será ["1", "2", "3"]
. Ten en cuenta que los valores de los parámetros siempre son cadenas; Rails no intenta adivinar o convertir el tipo.
NOTA: Valores como [nil]
o [nil, nil, ...]
en params
se reemplazan
con []
por razones de seguridad por defecto. Consulta la Guía de Seguridad
para más información.
Para enviar un hash, incluyes el nombre de la clave dentro de los corchetes:
<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>
Cuando se envía este formulario, el valor de params[:client]
será { "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }
. Nota el hash anidado en params[:client][:address]
.
El objeto params
actúa como un Hash, pero te permite usar símbolos y cadenas de manera intercambiable como claves.
4.2 Parámetros JSON
Si tu aplicación expone una API, es probable que aceptes parámetros en formato JSON. Si el encabezado "Content-Type" de tu solicitud está configurado como "application/json", Rails cargará automáticamente tus parámetros en el hash params
, al cual puedes acceder como lo harías normalmente.
Por ejemplo, si estás enviando este contenido JSON:
{ "company": { "name": "acme", "address": "123 Carrot Street" } }
Tu controlador recibirá params[:company]
como { "name" => "acme", "address" => "123 Carrot Street" }
.
Además, si has activado config.wrap_parameters
en tu inicializador o llamado a wrap_parameters
en tu controlador, puedes omitir de manera segura el elemento raíz en el parámetro JSON. En este caso, los parámetros se clonarán y envolverán con una clave elegida basada en el nombre de tu controlador. Así que la solicitud JSON anterior se puede escribir como:
{ "name": "acme", "address": "123 Carrot Street" }
Y, asumiendo que estás enviando los datos a CompaniesController
, entonces se envolvería dentro de la clave :company
así:
{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } }
Puedes personalizar el nombre de la clave o los parámetros específicos que deseas envolver consultando la documentación de la API.
NOTA: El soporte para el análisis de parámetros XML ha sido extraído a una gema llamada actionpack-xml_parser
.
4.3 Parámetros de Enrutamiento
El hash params
siempre contendrá las claves :controller
y :action
, pero debes usar los métodos controller_name
y action_name
en su lugar para acceder a estos valores. Cualquier otro parámetro definido por el enrutamiento, como :id
, también estará disponible. Como ejemplo, considera una lista de clientes donde la lista puede mostrar clientes activos o inactivos. Podemos agregar una ruta que capture el parámetro :status
en una URL "bonita":
get '/clients/:status', to: 'clients#index', foo: 'bar'
En este caso, cuando un usuario abre la URL /clients/active
, params[:status]
se establecerá en "active". Cuando se usa esta ruta, params[:foo]
también se establecerá en "bar", como si hubiera sido pasado en la cadena de consulta. Tu controlador también recibirá params[:action]
como "index" y params[:controller]
como "clients".
4.4 Parámetros de Clave Compuesta
Los parámetros de clave compuesta contienen múltiples valores en un solo parámetro. Por esta razón, necesitamos poder extraer cada valor y pasarlos a Active Record. Podemos aprovechar el método extract_value
para este caso de uso.
Dado el siguiente controlador:
class BooksController < ApplicationController
def show
# Extraer el valor de ID compuesto de los parámetros de URL.
id = params.extract_value(:id)
# Encontrar el libro usando el ID compuesto.
@book = Book.find(id)
# usar el comportamiento de renderizado predeterminado para renderizar la vista show.
end
end
Y la siguiente ruta:
get '/books/:id', to: 'books#show'
Cuando un usuario abre la URL /books/4_2
, el controlador extraerá el valor de clave compuesta ["4", "2"]
y lo pasará a Book.find
para renderizar el registro correcto en la vista. El método extract_value
puede ser usado para extraer arrays de cualquier parámetro delimitado.
4.5 default_url_options
Puedes establecer parámetros predeterminados globales para la generación de URLs definiendo un método llamado default_url_options
en tu controlador. Dicho método debe devolver un hash con los valores predeterminados deseados, cuyas claves deben ser símbolos:
class ApplicationController < ActionController::Base
def default_url_options
{ locale: I18n.locale }
end
end
Estas opciones se usarán como punto de partida al generar URLs, por lo que es posible que sean reemplazadas por las opciones pasadas a las llamadas url_for
.
Si defines default_url_options
en ApplicationController
, como en el ejemplo anterior, estos valores predeterminados se usarán para toda la generación de URLs. El método también se puede definir en un controlador específico, en cuyo caso solo afecta a las URLs generadas allí.
En una solicitud dada, el método no se llama realmente para cada URL generada. Por razones de rendimiento, el hash devuelto se almacena en caché, y hay como máximo una invocación por solicitud.
4.6 Parámetros Fuertes
Con los parámetros fuertes, los parámetros del Action Controller están prohibidos para ser usados en asignaciones masivas de Active Model hasta que hayan sido permitidos. Esto significa que tendrás que tomar una decisión consciente sobre qué atributos permitir para la actualización masiva. Esta es una mejor práctica de seguridad para ayudar a prevenir permitir accidentalmente que los usuarios actualicen atributos sensibles del modelo.
Además, los parámetros pueden ser marcados como requeridos y fluirán a través de un flujo de raise/rescue predefinido que resultará en un 400 Bad Request si no se pasan todos los parámetros requeridos.
class PeopleController < ActionController::Base
# Esto lanzará una excepción ActiveModel::ForbiddenAttributesError
# porque está usando asignación masiva sin un paso de permiso explícito.
def create
Person.create(params[:person])
end
# Esto pasará sin problemas siempre y cuando haya una clave person
# en los parámetros, de lo contrario lanzará una excepción
# ActionController::ParameterMissing, que será capturada por
# ActionController::Base y convertida en un error 400 Bad Request.
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end
private
# Usar un método privado para encapsular los parámetros permisibles
# es solo un buen patrón ya que podrás reutilizar la misma
# lista de permisos entre create y update. Además, puedes especializar
# este método con verificación por usuario de atributos permisibles.
def person_params
params.require(:person).permit(:name, :age)
end
end
4.6.1 Valores Escalares Permitidos
Llamar a permit
como:
params.permit(:id)
permite la inclusión de la clave especificada (:id
) si aparece en params
y tiene un valor escalar permitido asociado. De lo contrario, la clave será filtrada, por lo que no se pueden inyectar arrays, hashes u otros objetos.
Los tipos escalares permitidos son String
, Symbol
, NilClass
, Numeric
, TrueClass
, FalseClass
, Date
, Time
, DateTime
, StringIO
, IO
, ActionDispatch::Http::UploadedFile
, y Rack::Test::UploadedFile
.
Para declarar que el valor en params
debe ser un array de valores escalares permitidos, mapea la clave a un array vacío:
params.permit(id: [])
A veces no es posible o conveniente declarar las claves válidas de un parámetro hash o su estructura interna. Simplemente mapea a un hash vacío:
params.permit(preferences: {})
pero ten cuidado porque esto abre la puerta a entradas arbitrarias. En este caso, permit
asegura que los valores en la estructura devuelta sean escalares permitidos y filtra cualquier otra cosa.
Para permitir un hash completo de parámetros, se puede usar el método permit!
:
params.require(:log_entry).permit!
Esto marca el hash de parámetros :log_entry
y cualquier sub-hash de él como permitido y no verifica los escalares permitidos, se acepta cualquier cosa. Se debe tener extremo cuidado al usar permit!
, ya que permitirá que todos los atributos del modelo presentes y futuros sean asignados masivamente.
4.6.2 Parámetros Anidados
También puedes usar permit
en parámetros anidados, como:
params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])
Esta declaración permite los atributos name
, emails
, y friends
. Se espera que emails
sea un array de valores escalares permitidos, y que friends
sea un array de recursos con atributos específicos: deben tener un atributo name
(se permiten cualquier valor escalar permitido), un atributo hobbies
como un array de valores escalares permitidos, y un atributo family
que está restringido a tener un name
(también se permiten aquí cualquier valor escalar permitido).
4.6.3 Más Ejemplos
Es posible que también desees usar los atributos permitidos en tu acción new
. Esto plantea el problema de que no puedes usar require
en la clave raíz porque, normalmente, no existe cuando llamas a new
:
# usando `fetch` puedes proporcionar un valor predeterminado y usar
# la API de Parámetros Fuertes desde allí.
params.fetch(:blog, {}).permit(:title, :author)
El método de clase del modelo accepts_nested_attributes_for
te permite actualizar y destruir registros asociados. Esto se basa en los parámetros id
y _destroy
:
# permite :id y :_destroy
params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy])
Los hashes con claves enteras se tratan de manera diferente, y puedes declarar
los atributos como si fueran hijos directos. Obtienes estos tipos de
parámetros cuando usas accepts_nested_attributes_for
en combinación
con una asociación has_many
:
# Para permitir los siguientes datos:
# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}
params.require(:book).permit(:title, chapters_attributes: [:title])
Imagina un escenario donde tienes parámetros que representan un nombre de producto, y un hash de datos arbitrarios asociados con ese producto, y quieres permitir el atributo de nombre del producto y también todo el hash de datos:
def product_params
params.require(:product).permit(:name, data: {})
end
4.6.4 Fuera del Alcance de los Parámetros Fuertes
La API de parámetros fuertes fue diseñada con los casos de uso más comunes en mente. No está destinada a ser una solución mágica para manejar todos tus problemas de filtrado de parámetros. Sin embargo, puedes mezclar fácilmente la API con tu propio código para adaptarte a tu situación.
5 Sesión
Tu aplicación tiene una sesión para cada usuario en la que puedes almacenar pequeñas cantidades de datos que se mantendrán entre solicitudes. La sesión solo está disponible en el controlador y la vista y puede usar uno de varios mecanismos de almacenamiento diferentes:
ActionDispatch::Session::CookieStore
- Almacena todo en el cliente.ActionDispatch::Session::CacheStore
- Almacena los datos en la caché de Rails.ActionDispatch::Session::MemCacheStore
- Almacena los datos en un clúster de memcached (esta es una implementación heredada; considera usarCacheStore
en su lugar).ActionDispatch::Session::ActiveRecordStore
- Almacena los datos en una base de datos usando Active Record (requiere la gemaactiverecord-session_store
)- Un almacén personalizado o un almacén proporcionado por una gema de terceros
Todos los almacenes de sesión usan una cookie para almacenar un ID único para cada sesión (debes usar una cookie, Rails no te permitirá pasar el ID de sesión en la URL ya que esto es menos seguro).
Para la mayoría de los almacenes, este ID se usa para buscar los datos de la sesión en el servidor, por ejemplo, en una tabla de base de datos. Hay una excepción, y esa es el almacén de sesiones predeterminado y recomendado: el CookieStore, que almacena todos los datos de la sesión en la propia cookie (el ID sigue estando disponible para ti si lo necesitas). Esto tiene la ventaja de ser muy ligero, y no requiere ninguna configuración en una nueva aplicación para usar la sesión. Los datos de la cookie están firmados criptográficamente para hacerlos a prueba de manipulaciones. Y también están encriptados para que cualquiera con acceso a ellos no pueda leer su contenido. (Rails no lo aceptará si ha sido editado).
El CookieStore puede almacenar alrededor de 4 kB de datos, mucho menos que los otros, pero esto suele ser suficiente. Se desaconseja almacenar grandes cantidades de datos en la sesión sin importar qué almacén de sesión use tu aplicación. Debes evitar especialmente almacenar objetos complejos (como instancias de modelo) en la sesión, ya que el servidor podría no ser capaz de reensamblarlos entre solicitudes, lo que resultará en un error.
Si las sesiones de tu usuario no almacenan datos críticos o no necesitan estar disponibles durante largos períodos (por ejemplo, si solo usas el flash para mensajes), puedes considerar usar ActionDispatch::Session::CacheStore
. Esto almacenará las sesiones usando la implementación de caché que hayas configurado para tu aplicación. La ventaja de esto es que puedes usar tu infraestructura de caché existente para almacenar sesiones sin requerir ninguna configuración o administración adicional. La desventaja, por supuesto, es que las sesiones serán efímeras y podrían desaparecer en cualquier momento.
Lee más sobre el almacenamiento de sesiones en la Guía de Seguridad.
Si necesitas un mecanismo de almacenamiento de sesión diferente, puedes cambiarlo en un inicializador:
Rails.application.config.session_store :cache_store
Consulta config.session_store
en la
guía de configuración para más información.
Rails configura una clave de sesión (el nombre de la cookie) al firmar los datos de la sesión. Estos también se pueden cambiar en un inicializador:
# Asegúrate de reiniciar tu servidor cuando modifiques este archivo.
Rails.application.config.session_store :cookie_store, key: '_your_app_session'
También puedes pasar una clave :domain
y especificar el nombre de dominio para la cookie:
# Asegúrate de reiniciar tu servidor cuando modifiques este archivo.
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
Rails configura (para el CookieStore) una clave secreta utilizada para firmar los datos de la sesión en config/credentials.yml.enc
. Esto se puede cambiar con bin/rails credentials:edit
.
# aws:
# access_key_id: 123
# secret_access_key: 345
# Usado como la clave secreta base para todos los MessageVerifiers en Rails, incluyendo el que protege las cookies.
secret_key_base: 492f...
NOTA: Cambiar el secret_key_base cuando se usa el CookieStore
invalidará todas las sesiones existentes.
5.1 Accediendo a la Sesión
En tu controlador, puedes acceder a la sesión a través del método de instancia session
.
NOTA: Las sesiones se cargan de manera perezosa. Si no accedes a las sesiones en el código de tu acción, no se cargarán. Por lo tanto, nunca necesitarás deshabilitar las sesiones, simplemente no acceder a ellas hará el trabajo.
Los valores de la sesión se almacenan usando pares clave/valor como un hash:
class ApplicationController < ActionController::Base
private
# Encuentra al Usuario con el ID almacenado en la sesión con la clave
# :current_user_id Esta es una forma común de manejar el inicio de sesión del usuario en
# una aplicación Rails; iniciar sesión establece el valor de la sesión y
# cerrar sesión lo elimina.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by(id: session[:current_user_id])
end
end
Para almacenar algo en la sesión, simplemente asígnalo a la clave como un hash:
class LoginsController < ApplicationController
# "Crear" un inicio de sesión, también conocido como "iniciar sesión al usuario"
def create
if user = User.authenticate(params[:username], params[:password])
# Guarda el ID de usuario en la sesión para que pueda usarse en
# solicitudes posteriores
session[:current_user_id] = user.id
redirect_to root_url
end
end
end
Para eliminar algo de la sesión, elimina el par clave/valor:
class LoginsController < ApplicationController
# "Eliminar" un inicio de sesión, también conocido como "cerrar sesión al usuario"
def destroy
# Elimina el id de usuario de la sesión
session.delete(:current_user_id)
# Limpia el usuario actual memoizado
@_current_user = nil
redirect_to root_url, status: :see_other
end
end
Para restablecer toda la sesión, usa reset_session
.
5.2 El Flash
El flash es una parte especial de la sesión que se borra con cada solicitud. Esto significa que los valores almacenados allí solo estarán disponibles en la siguiente solicitud, lo cual es útil para pasar mensajes de error, etc.
El flash se accede a través del método flash
. Al igual que la sesión, el flash se representa como un hash.
Usemos el acto de cerrar sesión como ejemplo. El controlador puede enviar un mensaje que se mostrará al usuario en la siguiente solicitud:
class LoginsController < ApplicationController
def destroy
session.delete(:current_user_id)
flash[:notice] = "Has cerrado sesión exitosamente."
redirect_to root_url, status: :see_other
end
end
Nota que también es posible asignar un mensaje flash como parte de la redirección. Puedes asignar :notice
, :alert
o el :flash
de propósito general:
redirect_to root_url, notice: "Has cerrado sesión exitosamente."
redirect_to root_url, alert: "¡Estás atrapado aquí!"
redirect_to root_url, flash: { referral_code: 1234 }
La acción destroy
redirige a la root_url
de la aplicación, donde se mostrará el mensaje. Nota que depende completamente de la siguiente acción decidir qué, si acaso, hará con lo que la acción anterior puso en el flash. Es convencional mostrar cualquier alerta de error o aviso del flash en el layout de la aplicación:
<html>
<!-- <head/> -->
<body>
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
<!-- más contenido -->
</body>
</html>
De esta manera, si una acción establece un aviso o un mensaje de alerta, el layout lo mostrará automáticamente.
Puedes pasar cualquier cosa que la sesión pueda almacenar; no estás limitado a avisos y alertas:
<% if flash[:just_signed_up] %>
<p class="welcome">¡Bienvenido a nuestro sitio!</p>
<% end %>
Si deseas que un valor flash se mantenga para otra solicitud, usa flash.keep
:
class MainController < ApplicationController
# Digamos que esta acción corresponde a root_url, pero deseas
# que todas las solicitudes aquí sean redirigidas a UsersController#index.
# Si una acción establece el flash y redirige aquí, los valores
# normalmente se perderían cuando ocurra otra redirección, pero puedes
# usar 'keep' para hacer que persista para otra solicitud.
def index
# Persistirá todos los valores flash.
flash.keep
# También puedes usar una clave para mantener solo algún tipo de valor.
# flash.keep(:notice)
redirect_to users_url
end
end
5.2.1 flash.now
Por defecto, agregar valores al flash los hará disponibles para la próxima solicitud, pero a veces puedes querer acceder a esos valores en la misma solicitud. Por ejemplo, si la acción create
no logra guardar un recurso, y renderizas la plantilla new
directamente, eso no resultará en una nueva solicitud, pero es posible que aún desees mostrar un mensaje usando el flash. Para hacer esto, puedes usar flash.now
de la misma manera que usas el flash normal:
class ClientsController < ApplicationController
def create
@client = Client.new(client_params)
if @client.save
# ...
else
flash.now[:error] = "No se pudo guardar el cliente"
render action: "new"
end
end
end
6 Cookies
Tu aplicación puede almacenar pequeñas cantidades de datos en el cliente, llamadas cookies, que se mantendrán entre solicitudes e incluso sesiones. Rails proporciona un acceso fácil a las cookies a través del método cookies
, que, al igual que la session
, funciona como un hash:
class CommentsController < ApplicationController
def new
# Auto-completar el nombre del comentarista si se ha almacenado en una cookie
@comment = Comment.new(author: cookies[:commenter_name])
end
def create
@comment = Comment.new(comment_params)
if @comment.save
flash[:notice] = "¡Gracias por tu comentario!"
if params[:remember_name]
# Recordar el nombre del comentarista.
cookies[:commenter_name] = @comment.author
else
# Eliminar la cookie del nombre del comentarista, si la hay.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
else
render action: "new"
end
end
end
Ten en cuenta que, mientras que para los valores de sesión puedes establecer la clave en nil
, para eliminar un valor de cookie debes usar cookies.delete(:key)
.
Rails también proporciona un tarro de cookies firmado y un tarro de cookies encriptado para almacenar datos sensibles. El tarro de cookies firmado añade una firma criptográfica a los valores de las cookies para proteger su integridad. El tarro de cookies encriptado encripta los valores además de firmarlos, para que no puedan ser leídos por el usuario final. Consulta la documentación de la API para más detalles.
Estos tarros de cookies especiales usan un serializador para serializar los valores asignados en
cadenas y deserializarlos en objetos Ruby al leerlos. Puedes especificar qué
serializador usar a través de config.action_dispatch.cookies_serializer
.
El serializador predeterminado para nuevas aplicaciones es :json
. Ten en cuenta que JSON tiene
soporte limitado para redondear objetos Ruby. Por ejemplo, los objetos Date
, Time
y
Symbol
(incluidas las claves de Hash
) se serializarán y deserializarán
en String
s:
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
Si necesitas almacenar estos o más objetos complejos, es posible que necesites convertir manualmente sus valores al leerlos en solicitudes posteriores.
Si usas el almacén de sesión de cookies, lo anterior también se aplica al hash session
y
flash
.
7 Renderizado
ActionController hace que el renderizado de datos HTML, XML o JSON sea sencillo. Si has generado un controlador usando scaffolding, se vería algo así:
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
Puedes notar en el código anterior que estamos usando render xml: @users
, no render xml: @users.to_xml
. Si el objeto no es una cadena, Rails invocará automáticamente to_xml
por nosotros.
Puedes aprender más sobre el renderizado en la Guía de Layouts y Renderizado.
8 Callbacks de Acción
Los callbacks de acción son métodos que se ejecutan "antes", "después" o "alrededor" de una acción del controlador.
Los callbacks de acción se heredan, por lo que si configuras uno en un ApplicationController
, se ejecutará en cada controlador de tu aplicación.
Los callbacks de acción "antes" se registran a través de before_action
. Pueden detener el ciclo de solicitud. Un callback de acción "antes" común es uno que requiere que un usuario esté registrado para que una acción se ejecute. Puedes definir el método de esta manera:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "Debes estar registrado para acceder a esta sección"
redirect_to new_login_url # detiene el ciclo de solicitud
end
end
end
El método simplemente almacena un mensaje de error en el flash y redirige al formulario de inicio de sesión si el usuario no está registrado. Si un callback de acción "antes" renderiza o redirige, la acción del controlador no se ejecutará. Si hay callbacks de acción adicionales programados para ejecutarse después de ese, también se cancelan.
En este ejemplo, el callback de acción se agrega a ApplicationController
y, por lo tanto, todos los controladores en la aplicación lo heredan. Esto hará que todo en la aplicación requiera que el usuario esté registrado para usarlo. Por razones obvias (¡el usuario no podría iniciar sesión en primer lugar!), no todos los controladores o acciones deberían requerir esto. Puedes evitar que este callback de acción se ejecute antes de acciones particulares con skip_before_action
:
class LoginsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
Ahora, las acciones new
y create
de LoginsController
funcionarán como antes sin requerir que el usuario esté registrado. La opción :only
se usa para omitir este callback de acción solo para estas acciones, y también hay una opción :except
que funciona al revés. Estas opciones también se pueden usar al agregar callbacks de acción, por lo que puedes agregar un callback que solo se ejecute para acciones seleccionadas en primer lugar.
NOTA: Llamar al mismo callback de acción varias veces con diferentes opciones no funcionará, ya que la última definición del callback de acción sobrescribirá las anteriores.
8.1 Callbacks de Acción Después y Alrededor
Además del callback de acción "antes", también puedes ejecutar callbacks de acción después de que se haya ejecutado una acción del controlador, o tanto antes como después.
Los callbacks de acción "después" se registran a través de after_action
. Son similares a los callbacks de acción "antes", pero debido a que la acción del controlador ya se ha ejecutado, tienen acceso a los datos de respuesta que están a punto de ser enviados al cliente. Obviamente, los callbacks de acción "después" no pueden detener la ejecución de la acción. Ten en cuenta que los callbacks de acción "después" se ejecutan solo después de una acción de controlador exitosa, pero no cuando se produce una excepción en el ciclo de solicitud.
Los callbacks de acción "alrededor" se registran a través de around_action
. Son responsables de ejecutar sus acciones asociadas al ceder, similar a cómo funcionan los middlewares de Rack.
Por ejemplo, en un sitio web donde los cambios tienen un flujo de trabajo de aprobación, un administrador podría previsualizarlos fácilmente aplicándolos dentro de una transacción:
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
Ten en cuenta que un callback de acción "alrededor" también envuelve la renderización. En particular, en el ejemplo anterior, si la propia vista lee de la base de datos (por ejemplo, a través de un scope), lo hará dentro de la transacción y, por lo tanto, presentará los datos para previsualizar.
Puedes optar por no ceder y construir la respuesta tú mismo, en cuyo caso la acción del controlador no se ejecutará.
8.2 Otras Formas de Usar Callbacks de Acción
Si bien la forma más común de usar callbacks de acción es creando métodos privados y usando before_action
, after_action
o around_action
para agregarlos, hay otras dos formas de hacer lo mismo.
La primera es usar un bloque directamente con los métodos *_action
. El bloque recibe el controlador como argumento. El callback de acción require_login
del ejemplo anterior podría reescribirse para usar un bloque:
class ApplicationController < ActionController::Base
before_action do |controller|
unless controller.send(:logged_in?)
flash[:error] = "Debes estar registrado para acceder a esta sección"
redirect_to new_login_url
end
end
end
Ten en cuenta que el callback de acción, en este caso, usa send
porque el método logged_in?
es privado, y el callback de acción no se ejecuta en el ámbito del controlador. Esta no es la forma recomendada de implementar este callback de acción en particular, pero en casos más simples, podría ser útil.
Específicamente para around_action
, el bloque también cede en la action
:
around_action { |_controller, action| time(&action) }
La segunda forma es usar una clase (en realidad, cualquier objeto que responda a los métodos correctos servirá) para manejar la acción del callback. Esto es útil en casos que son más complejos y no pueden ser implementados de una manera legible y reutilizable usando los otros dos métodos. Como ejemplo, podrías reescribir el callback de acción de inicio de sesión nuevamente para usar una clase:
class ApplicationController < ActionController::Base
before_action LoginActionCallback
end
class LoginActionCallback
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "Debes estar registrado para acceder a esta sección"
controller.redirect_to controller.new_login_url
end
end
end
Nuevamente, este no es un ejemplo ideal para este callback de acción, porque no se ejecuta en el ámbito del controlador, pero recibe el controlador como argumento. La clase debe implementar un método con el mismo nombre que el callback de acción, por lo que para el callback de acción before_action
, la clase debe implementar un método before
, y así sucesivamente. El método around
debe ceder
para ejecutar la acción.
9 Protección Contra Falsificación de Solicitudes
La falsificación de solicitudes entre sitios es un tipo de ataque en el que un sitio engaña a un usuario para que realice solicitudes en otro sitio, posiblemente agregando, modificando o eliminando datos en ese sitio sin el conocimiento o permiso del usuario.
El primer paso para evitar esto es asegurarse de que todas las acciones "destructivas" (crear, actualizar y destruir) solo se puedan acceder con solicitudes que no sean GET. Si estás siguiendo las convenciones RESTful ya estás haciendo esto. Sin embargo, un sitio malicioso aún puede enviar una solicitud que no sea GET a tu sitio con bastante facilidad, y ahí es donde entra la protección contra la falsificación de solicitudes. Como su nombre indica, protege de solicitudes falsificadas.
La forma en que se hace esto es agregar un token no adivinable que solo es conocido por tu servidor a cada solicitud. De esta manera, si llega una solicitud sin el token adecuado, se le negará el acceso.
Si generas un formulario como este:
<%= form_with model: @user do |form| %>
<%= form.text_field :username %>
<%= form.text_field :password %>
<% end %>
Verás cómo se agrega el token como un campo oculto:
<form accept-charset="UTF-8" action="/users/1" method="post">
<input type="hidden"
value="67250ab105eb5ad10851c00a5621854a23af5489"
name="authenticity_token"/>
<!-- campos -->
</form>
Rails agrega este token a cada formulario que se genera usando los ayudantes de formulario, por lo que la mayoría de las veces no tienes que preocuparte por ello. Si estás escribiendo un formulario manualmente o necesitas agregar el token por otra razón, está disponible a través del método form_authenticity_token
:
El form_authenticity_token
genera un token de autenticidad válido. Eso es útil en lugares donde Rails no lo agrega automáticamente, como en llamadas Ajax personalizadas.
La Guía de Seguridad tiene más sobre esto, y muchos otros temas relacionados con la seguridad que debes conocer al desarrollar una aplicación web.
10 Los Objetos de Solicitud y Respuesta
En cada controlador, hay dos métodos de acceso que apuntan a los objetos de solicitud y respuesta asociados con el ciclo de solicitud que se está ejecutando actualmente. El método request
contiene una instancia de ActionDispatch::Request
y el método response
devuelve un objeto de respuesta que representa lo que se va a enviar de vuelta al cliente.
10.1 El Objeto request
El objeto de solicitud contiene mucha información útil sobre la solicitud que viene del cliente. Para obtener una lista completa de los métodos disponibles, consulta la documentación de la API de Rails y la Documentación de Rack. Entre las propiedades a las que puedes acceder en este objeto están:
Propiedad de request |
Propósito |
---|---|
host |
El nombre de host utilizado para esta solicitud. |
domain(n=2) |
Los primeros n segmentos del nombre de host, comenzando desde la derecha (el TLD). |
format |
El tipo de contenido solicitado por el cliente. |
method |
El método HTTP utilizado para la solicitud. |
get? , post? , patch? , put? , delete? , head? |
Devuelve verdadero si el método HTTP es GET/POST/PATCH/PUT/DELETE/HEAD. |
headers |
Devuelve un hash que contiene los encabezados asociados con la solicitud. |
port |
El número de puerto (entero) utilizado para la solicitud. |
protocol |
Devuelve una cadena que contiene el protocolo utilizado más "://", por ejemplo "http://". |
query_string |
La parte de la cadena de consulta de la URL, es decir, todo lo que está después de "?". |
remote_ip |
La dirección IP del cliente. |
url |
La URL completa utilizada para la solicitud. |
10.1.1 path_parameters
, query_parameters
, y request_parameters
Rails recopila todos los parámetros enviados junto con la solicitud en el hash params
, ya sea que se envíen como parte de la cadena de consulta o del cuerpo de la publicación. El objeto de solicitud tiene tres métodos de acceso que te dan acceso a estos parámetros dependiendo de dónde provienen. El hash query_parameters
contiene parámetros que se enviaron como parte de la cadena de consulta, mientras que el hash request_parameters
contiene parámetros enviados como parte del cuerpo de la publicación. El hash path_parameters
contiene parámetros que fueron reconocidos por el enrutamiento como parte de la ruta que lleva a este controlador y acción en particular.
10.2 El Objeto response
El objeto de respuesta no se usa generalmente directamente, pero se construye durante la ejecución de la acción y la renderización de los datos que se están enviando de vuelta al usuario, pero a veces, como en un callback de acción posterior, puede ser útil acceder a la respuesta directamente. Algunos de estos métodos de acceso también tienen setters, lo que te permite cambiar sus valores. Para obtener una lista completa de los métodos disponibles, consulta la documentación de la API de Rails y la Documentación de Rack.
Propiedad de response |
Propósito |
---|---|
body |
Esta es la cadena de datos que se envía de vuelta al cliente. Generalmente es HTML. |
status |
El código de estado HTTP para la respuesta, como 200 para una solicitud exitosa o 404 para archivo no encontrado. |
location |
La URL a la que se está redirigiendo al cliente, si la hay. |
content_type |
El tipo de contenido de la respuesta. |
charset |
El conjunto de caracteres que se está utilizando para la respuesta. El valor predeterminado es "utf-8". |
headers |
Encabezados utilizados para la respuesta. |
10.2.1 Estableciendo Encabezados Personalizados
Si deseas establecer encabezados personalizados para una respuesta, entonces response.headers
es el lugar para hacerlo. El atributo headers es un hash que mapea nombres de encabezados a sus valores, y Rails configurará algunos de ellos automáticamente. Si deseas agregar o cambiar un encabezado, simplemente asígnalo a response.headers
de esta manera:
response.headers["Content-Type"] = "application/pdf"
NOTA: En el caso anterior, tendría más sentido usar directamente el setter content_type
.
11 Autenticaciones HTTP
Rails viene con tres mecanismos de autenticación HTTP incorporados:
- Autenticación Básica
- Autenticación Digest
- Autenticación de Token
11.1 Autenticación Básica HTTP
La autenticación básica HTTP es un esquema de autenticación que es compatible con la mayoría de los navegadores y otros clientes HTTP. Como ejemplo, considera una sección de administración que solo estará disponible ingresando un nombre de usuario y una contraseña en la ventana de diálogo básica HTTP del navegador. Usar la autenticación incorporada solo requiere que uses un método, http_basic_authenticate_with
.
class AdminsController < ApplicationController
http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end
Con esto en su lugar, puedes crear controladores con espacio de nombres que hereden de AdminsController
. El callback de acción se ejecutará así para todas las acciones en esos controladores, protegiéndolos con autenticación básica HTTP.
11.2 Autenticación Digest HTTP
La autenticación digest HTTP es superior a la autenticación básica ya que no requiere que el cliente envíe una contraseña sin cifrar a través de la red (aunque la autenticación básica HTTP es segura a través de HTTPS). Usar la autenticación digest con Rails solo requiere usar un 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 se ve en el ejemplo anterior, el bloque authenticate_or_request_with_http_digest
toma solo un argumento: el nombre de usuario. Y el bloque devuelve la contraseña. Devolver false
o nil
de authenticate_or_request_with_http_digest
causará un fallo en la autenticación.
11.3 Autenticación de Token HTTP
La autenticación de token HTTP es un esquema para habilitar el uso de tokens Bearer en el encabezado HTTP Authorization
. Existen muchos formatos de token disponibles y describirlos está fuera del alcance de este documento.
Como ejemplo, supongamos que deseas usar un token de autenticación que ha sido emitido de antemano para realizar autenticación y acceso. Implementar la autenticación de token con Rails solo requiere usar un 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 se ve en el ejemplo anterior, el bloque authenticate_or_request_with_http_token
toma dos argumentos: el token y un Hash
que contiene las opciones que fueron analizadas del encabezado HTTP Authorization
. El bloque debe devolver true
si la autenticación es exitosa. Devolver false
o nil
en él causará un fallo en la autenticación.
12 Transmisión y Descarga de Archivos
A veces puedes querer enviar un archivo al usuario en lugar de renderizar una página HTML. Todos los controladores en Rails tienen los métodos send_data
y send_file
, que transmitirán datos al cliente. send_file
es un método de conveniencia que te permite proporcionar el nombre de un archivo en el disco, y transmitirá el contenido de ese archivo por ti.
Para transmitir datos al cliente, usa send_data
:
require "prawn"
class ClientsController < ApplicationController
# Genera un documento PDF con información sobre el cliente y
# lo devuelve. El usuario obtendrá el PDF como una descarga de archivo.
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 "Dirección: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
La acción download_pdf
en el ejemplo anterior llamará a un método privado que realmente genera el documento PDF y lo devuelve como una cadena. Esta cadena luego se transmitirá al cliente como una descarga de archivo, y se sugerirá un nombre de archivo al usuario. A veces, cuando transmites archivos al usuario, es posible que no desees que descarguen el archivo. Toma las imágenes, por ejemplo, que pueden ser incrustadas en páginas HTML. Para decirle al navegador que un archivo no está destinado a ser descargado, puedes establecer la opción :disposition
en "inline". El valor opuesto y predeterminado para esta opción es "attachment".
12.1 Enviando Archivos
Si deseas enviar un archivo que ya existe en el disco, usa el método send_file
.
class ClientsController < ApplicationController
# Transmite un archivo que ya ha sido generado y almacenado en el 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
Esto leerá y transmitirá el archivo 4 kB a la vez, evitando cargar todo el archivo en la memoria de una vez. Puedes desactivar la transmisión con la opción :stream
o ajustar el tamaño del bloque con la opción :buffer_size
.
Si no se especifica :type
, se adivinará a partir de la extensión del archivo especificada en :filename
. Si el tipo de contenido no está registrado para la extensión, se usará application/octet-stream
.
ADVERTENCIA: Ten cuidado al usar datos provenientes del cliente (params, cookies, etc.) para ubicar el archivo en el disco, ya que esto es un riesgo de seguridad que podría permitir a alguien acceder a archivos a los que no deberían acceder.
CONSEJO: No se recomienda que transmitas archivos estáticos a través de Rails si puedes mantenerlos en una carpeta pública en tu servidor web. Es mucho más eficiente permitir que el usuario descargue el archivo directamente usando Apache u otro servidor web, evitando que la solicitud pase innecesariamente por toda la pila de Rails.
12.2 Descargas RESTful
Aunque send_data
funciona bien, si estás creando una aplicación RESTful, generalmente no es necesario tener acciones separadas para descargas de archivos. En la terminología REST, el archivo PDF del ejemplo anterior puede considerarse solo otra representación del recurso cliente. Rails proporciona una forma elegante de hacer descargas "RESTful". Aquí está cómo puedes reescribir el ejemplo para que la descarga del PDF sea parte de la acción show
, sin ninguna transmisión:
class ClientsController < ApplicationController
# El usuario puede solicitar recibir este recurso como HTML o 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 ejemplo funcione, debes agregar el tipo MIME PDF a Rails. Esto se puede hacer agregando la siguiente línea al archivo config/initializers/mime_types.rb
:
Mime::Type.register "application/pdf", :pdf
NOTA: Los archivos de configuración no se recargan en cada solicitud, por lo que debes reiniciar el servidor para que sus cambios surtan efecto.
Ahora el usuario puede solicitar obtener una versión en PDF de un cliente simplemente agregando ".pdf" a la URL:
GET /clients/1.pdf
12.3 Transmisión en Vivo de Datos Arbitrarios
Rails te permite transmitir más que solo archivos. De hecho, puedes transmitir cualquier cosa
que desees en un objeto de respuesta. El módulo ActionController::Live
te permite
crear una conexión persistente con un navegador. Usando este módulo, podrás
enviar datos arbitrarios al navegador en puntos específicos en el tiempo.
12.3.1 Incorporando Transmisión en Vivo
Incluir ActionController::Live
dentro de tu clase de controlador proporcionará
a todas las acciones dentro del controlador la capacidad de transmitir datos. Puedes mezclar
el módulo de esta manera:
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
El código anterior mantendrá una conexión persistente con el navegador y enviará 100
mensajes de "hello world\n"
, cada uno con un segundo de diferencia.
Hay un par de cosas a notar en el ejemplo anterior. Necesitamos asegurarnos de cerrar el flujo de respuesta. Olvidar cerrar el flujo dejará
el socket abierto para siempre. También tenemos que establecer el tipo de contenido en text/event-stream
antes de escribir en el flujo de respuesta. Esto se debe a que los encabezados no pueden ser escritos
después de que la respuesta ha sido comprometida (cuando response.committed?
devuelve un valor verdadero),
lo cual ocurre cuando escribes o comprometes el flujo de respuesta.
12.3.2 Ejemplo de Uso
Supongamos que estás haciendo una máquina de Karaoke, y un usuario quiere obtener la
letra de una canción en particular. Cada Song
tiene un número particular de líneas y
cada línea toma tiempo num_beats
para terminar de cantar.
Si quisiéramos devolver las letras en estilo Karaoke (solo enviando la línea cuando
el cantante ha terminado la línea anterior), entonces podríamos usar ActionController::Live
de la siguiente manera:
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
El código anterior envía la siguiente línea solo después de que el cantante ha completado la línea anterior.
12.3.3 Consideraciones de Transmisión
La transmisión de datos arbitrarios es una herramienta extremadamente poderosa. Como se muestra en los ejemplos anteriores, puedes elegir cuándo y qué enviar a través de un flujo de respuesta. Sin embargo, también debes tener en cuenta las siguientes cosas:
- Cada flujo de respuesta crea un nuevo hilo y copia las variables locales del hilo del hilo original. Tener demasiadas variables locales del hilo puede impactar negativamente en el rendimiento. De manera similar, un gran número de hilos también puede afectar el rendimiento.
- No cerrar el flujo de respuesta dejará el socket correspondiente abierto para siempre. Asegúrate de llamar a
close
siempre que estés usando un flujo de respuesta. - Los servidores WEBrick almacenan en búfer todas las respuestas, y por lo tanto incluir
ActionController::Live
no funcionará. Debes usar un servidor web que no almacene en búfer automáticamente las respuestas.
13 Filtrado de Registro
Rails mantiene un archivo de registro para cada entorno en la carpeta log
. Estos son extremadamente útiles al depurar lo que realmente está sucediendo en tu aplicación, pero en una aplicación en vivo es posible que no desees que cada bit de información se almacene en el archivo de registro.
13.1 Filtrado de Parámetros
Puedes filtrar parámetros de solicitud sensibles de tus archivos de registro
agregándolos a config.filter_parameters
en la configuración de la aplicación.
Estos parámetros serán marcados como [FILTERED] en el registro.
config.filter_parameters << :password
NOTA: Los parámetros proporcionados serán filtrados por expresión regular de coincidencia parcial. Rails agrega una lista de filtros predeterminados, incluidos :passw
,
:secret
, y :token
, en el inicializador apropiado
(initializers/filter_parameter_logging.rb
) para manejar parámetros típicos de la aplicación como password
, password_confirmation
y my_token
.
13.2 Filtrado de Redirecciones
A veces es deseable filtrar de los archivos de registro algunas ubicaciones sensibles a las que tu aplicación se está redirigiendo.
Puedes hacer eso usando la opción de configuración config.filter_redirect
:
config.filter_redirect << 's3.amazonaws.com'
Puedes configurarlo como una Cadena, una Expresión Regular o un array de ambas.
config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]
Las URLs coincidentes serán reemplazadas con '[FILTERED]'. Sin embargo, si solo deseas filtrar los parámetros, no las URLs completas, por favor consulta Filtrado de Parámetros.
14 Rescate
Lo más probable es que tu aplicación contenga errores o de otro modo arroje una excepción que necesita ser manejada. Por ejemplo, si el usuario sigue un enlace a un recurso que ya no existe en la base de datos, Active Record lanzará la excepción ActiveRecord::RecordNotFound
.
El manejo de excepciones predeterminado de Rails muestra un mensaje de "500 Error del Servidor" para todas las excepciones. Si la solicitud se realizó localmente, se muestra un buen seguimiento de la pila y se agrega información adicional, para que puedas averiguar qué salió mal y solucionarlo. Si la solicitud fue remota, Rails simplemente mostrará un mensaje simple de "500 Error del Servidor" al usuario, o un "404 No Encontrado" si hubo un error de enrutamiento, o no se pudo encontrar un registro. A veces es posible que desees personalizar cómo se capturan estos errores y cómo se muestran al usuario. Hay varios niveles de manejo de excepciones disponibles en una aplicación Rails:
14.1 Las Plantillas Predeterminadas 500 y 404
Por defecto, en el entorno de producción la aplicación renderizará un mensaje de error 404 o 500. En el entorno de desarrollo, todas las excepciones no manejadas simplemente se lanzan. Estos mensajes están contenidos en archivos HTML estáticos en la carpeta pública, en 404.html
y 500.html
respectivamente. Puedes personalizar estos archivos para agregar información y estilo adicional, pero recuerda que son HTML estático; es decir, no puedes usar ERB, SCSS, CoffeeScript o layouts para ellos.
14.2 rescue_from
Si deseas hacer algo un poco más elaborado al capturar errores, puedes usar rescue_from
, que maneja excepciones de un cierto tipo (o múltiples tipos) en un controlador completo y sus subclases.
Cuando ocurre una excepción que es capturada por una directiva rescue_from
, el objeto de excepción se pasa al manejador. El manejador puede ser un método o un objeto Proc
pasado a la opción :with
. También puedes usar un bloque directamente en lugar de un objeto Proc
explícito.
Aquí está cómo puedes usar rescue_from
para interceptar todos los errores ActiveRecord::RecordNotFound
y hacer algo con ellos.
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
Por supuesto, este ejemplo es cualquier cosa menos elaborado y no mejora en absoluto el manejo de excepciones predeterminado, pero una vez que puedes capturar todas esas excepciones, eres libre de hacer lo que quieras con ellas. Por ejemplo, podrías crear clases de excepción personalizadas que se lanzarán cuando un usuario no tenga acceso a una cierta sección de tu aplicación:
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :user_not_authorized
private
def user_not_authorized
flash[:error] = "No tienes acceso a esta sección."
redirect_back(fallback_location: root_path)
end
end
class ClientsController < ApplicationController
# Verifica que el usuario tenga la autorización correcta para acceder a los clientes.
before_action :check_authorization
# Nota cómo las acciones no tienen que preocuparse por todo el asunto de la autenticación.
def edit
@client = Client.find(params[:id])
end
private
# Si el usuario no está autorizado, simplemente lanza la excepción.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
end
ADVERTENCIA: Usar rescue_from
con Exception
o StandardError
podría causar efectos secundarios graves, ya que evita que Rails maneje las excepciones correctamente. Por lo tanto, no se recomienda hacerlo a menos que haya una razón de peso.
NOTA: Cuando se ejecuta en el entorno de producción, todos los
errores ActiveRecord::RecordNotFound
renderizan la página de error 404. A menos que necesites
un comportamiento personalizado, no necesitas manejar esto.
NOTA: Ciertas excepciones solo son rescatables desde la clase ApplicationController
, ya que se lanzan antes de que el controlador se inicialice y la acción se ejecute.
15 Forzar el Protocolo HTTPS
Si deseas asegurarte de que la comunicación con tu controlador solo sea posible
a través de HTTPS, debes hacerlo habilitando el middleware ActionDispatch::SSL
a través de
config.force_ssl
en la configuración de tu entorno.
16 Punto Final de Verificación de Salud Incorporado
Rails también viene con un punto final de verificación de salud incorporado que es accesible en la ruta /up
. Este punto final devolverá un código de estado 200 si la aplicación se ha iniciado sin excepciones, y un código de estado 500 de lo contrario.
En producción, muchas aplicaciones están obligadas a informar su estado a aguas arriba, ya sea a un monitor de tiempo de actividad que notificará a un ingeniero cuando las cosas vayan mal, o a un equilibrador de carga o controlador de Kubernetes utilizado para determinar la salud de un pod. Esta verificación de salud está diseñada para ser un ajuste único que funcionará en muchas situaciones.
Mientras que cualquier aplicación Rails recién generada tendrá la verificación de salud en /up
, puedes configurar la ruta para que sea cualquier cosa que desees en tu "config/routes.rb":
Rails.application.routes.draw do
get "healthz" => "rails/health#show", as: :rails_health_check
end
La verificación de salud ahora será accesible a través de la ruta /healthz
.
NOTA: Este punto final no refleja el estado de todas las dependencias de tu aplicación, como la base de datos o el clúster de redis. Reemplaza "rails/health#show" con tu propia acción de controlador si tienes necesidades específicas de la aplicación.
Piensa cuidadosamente sobre lo que deseas verificar, ya que puede llevar a situaciones en las que tu aplicación se reinicia debido a que un servicio de terceros falla. Idealmente, deberías diseñar tu aplicación para manejar esas interrupciones de manera elegante.
Comentarios
Se te anima a ayudar a mejorar la calidad de esta guía.
Por favor contribuye si ves algún error tipográfico o errores fácticos. Para comenzar, puedes leer nuestra sección de contribuciones a la documentación.
También puedes encontrar contenido incompleto o cosas que no están actualizadas. Por favor agrega cualquier documentación faltante para main. Asegúrate de revisar Guías Edge primero para verificar si los problemas ya están resueltos o no en la rama principal. Revisa las Guías de Ruby on Rails para estilo y convenciones.
Si por alguna razón detectas algo que corregir pero no puedes hacerlo tú mismo, por favor abre un issue.
Y por último, pero no menos importante, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.