rails 4: páginas de error personalizadas para 404, 500 y ¿de dónde viene el mensaje de error predeterminado 500?

Actualmente en producción estoy obteniendo este texto:

500 Internal Server Error If you are the administrator of this website, then please read this web application's log file and/or the web server's log file to find out what went wrong. 

No html en esa página nada.

¿Dónde está este código? No tengo public / 500.html ni nada en ese sentido.

En mis rutas tengo:

  get "/404", :to => "errors#error_404" get "/422", :to => "errors#error_404" get "/500", :to => "errors#error_500" get "/505", :to => "errors#error_505" 

ErrorsController:

 class ErrorsController  404, :formats => [:html], :layout => "white", :sub_layout => "left" end def error_422 render :status => 422, :formats => [:html], :layout => "white", :sub_layout => "left" end def error_500 render :status => 500, :formats => [:html], :layout => "white", :sub_layout => "left" end def error_505 render :status => 505, :formats => [:html], :layout => "white", :sub_layout => "left" end end 

¿Cómo hacer que cargue mis errores personalizados siempre? En algunos errores, simplemente tira el texto de 2 líneas desde algún lugar del núcleo del riel, ¡quiero que recoja mis páginas de error personalizadas cada vez! ¿cómo? ¡Gracias!

El error que estás experimentando se está lanzando desde

https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L18-L22

Esto significa que el código que sus excepciones están siendo rescatadas por ellos mismos están lanzando excepciones. Puede consultar sus registros para ver el texto:

Error during failsafe response:

para identificar de qué se originan realmente las excepciones y así resolver su problema.

📆 Actualización 2018 📆

Nuestra joya exception_handler ahora se considera la más popular (páginas de error personalizadas de Rails) ↴

enter image description here enter image description here


Cómo funciona

Todas las excepciones de Rails se manejan con config.exceptions_app . Esto se asigna en los archivos config/application.rb o config/environments/*.rb ; debe ser una callback:

enter image description here

Cuando Rails acierta un error, invoca el middleware ShowExceptions . Llama a exception_app y envía la request completa (incluida la exception ) a exceptions_app :

Excepciones de Middleware-Powered

exceptions_app necesita entregar una respuesta . Si no, se carga el failsafe :

  # show_exceptions.rb#L38 def render_exception(env, exception) wrapper = ExceptionWrapper.new(env, exception) status = wrapper.status_code env["action_dispatch.exception"] = wrapper.exception env["PATH_INFO"] = "/#{status}" response = @exceptions_app.call(request.env) # => exceptions_app callback response[1]["X-Cascade"] == "pass" ? pass_response(status) : response rescue Exception => failsafe_error # => raised if exceptions_app false $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}" FAILSAFE_RESPONSE end 

El failsafe se almacena como FAILSAFE_RESPONSE en la parte superior de ShowExceptions .


Páginas de error personalizadas

Si desea crear páginas de error personalizadas, debe config.exceptions_app su propia callback en config.exceptions_app . Esto se puede hacer en la aplicación o con una gem:

enter image description here

Observe cómo se usa el método de call : así es como funciona una callback. Rails ( env ) se invoca cuando la solicitud se recibe de Internet; cuando se produce una excepción, env se pasa a exceptions_app .

La calidad de su manejo de excepciones dependerá de cómo administre env . Esto es importante; hacer referencia a self.routes no lleva el entorno hacia adelante.

La mejor manera es manejar excepciones con un controlador por separado. Esto le permite manejar la solicitud como si fuera simplemente otra vista, otorgando acceso al layout y otros componentes ( model / email ).

Hay dos formas de manejar excepciones:

  1. Anulando las rutas 404/500
  2. Invocando un controlador

Nuestra gem fue diseñada alrededor de nuestro controller , invocada cada vez que se exception una exception . Esto le da un control total sobre el proceso de excepción, lo que permite un diseño 100% de marca :

enter image description here

ExceptionHandler es ahora la principal joya de páginas de error personalizadas de producción para Rails.

Mantenido por más de 3 años, es la joya de excepción más simple y poderosa para Rails. Funciona al 100% en Rails 5 y ya se ha descargado más de 70,000 veces.


Joya

La última 0.8.0.0 tiene las siguientes actualizaciones:

  • Excepciones personalizadas
  • “Mapeo” de excepciones (elija qué excepciones manejar)
  • Notificaciónes de Correo Electrónico
  • Backend del modelo
  • Integración de piñones 4+
  • Suite de prueba RSpec
  • Vistas basadas en la configuración regional

Puedes leer más aquí .


Gestión de excepciones de Rails

Si no estás interesado en la gem, déjame explicarte el proceso:

Todas las excepciones de Rails se manejan con la config.exceptions_app llamada config.exceptions_app . Esto se asigna en los archivos config/application.rb o config/environments/*.rb ; debe ser una callback:

enter image description here

Siempre que su aplicación presente una excepción, se invoca el middleware ShowExceptions . Este middleware crea la excepción en la request y la reenvía a la config.exceptions_app llamada config.exceptions_app .

Por defecto, config.exceptions_app apunta a las rutas. Es por esto que Rails viene con 404.html , 500.html y 422.html en la carpeta public .

Si desea crear páginas de excepción personalizadas , debe anular la config.exceptions_app llamada config.exceptions_app , pasar la solicitud errónea a un controlador apropiado, ya sea un controller o una route :

[[middleware]]

Las dos formas de administrar esto de manera efectiva son enviar las solicitudes erróneas a las rutas o invocar un controlador.

La forma más simple, y más común, es reenviar la solicitud a las rutas; desafortunadamente, esto ignora la solicitud y le impide detallar las excepciones correctamente.

La mejor manera es invocar un controlador por separado. Esto le permitirá pasar la solicitud completa, lo que le permite guardarla, enviarla por correo electrónico o hacer otras cosas.

400/500 errores

Los raíles solo pueden responder con errores HTTP válidos .

Si bien la excepción de la aplicación puede ser diferente, el código de estado devuelto debe ser 40x o 50x . Esto está en línea con la especificación HTTP, y se describe aquí ↴

enter image description here

Esto significa que no importa qué solución de manejo de excepciones use / compile, Rails debe devolver errores 40x o 50x al navegador.

En otras palabras, las páginas de error personalizadas tienen poco que ver con el tipo de excepción: más información sobre cómo atrapa y atiende la respuesta del navegador .

De forma predeterminada, Rails lo hace con los 404.html , 422.html y 500.html en la carpeta public . Si desea manejar el flujo de excepciones usted mismo, necesita eliminar estos archivos y canalizar las solicitudes erróneas a su propia callback exceptions_app .

Esto se puede hacer con las routes o con un controller (que explicaré ahora):


1. Rutas

La forma más simple es dejar que las rutas lo manejen.

Este método está inflado y requiere el uso de múltiples acciones. También es difícil manejar las respuestas.

Este tutorial explica:

enter image description here

Esto muestra cómo reemplazar el exceptions_app con las rutas directamente:

 # config/application.rb config.exceptions_app = self.routes 

Aquí está el código que tengo (Ruby 2.0.0, Rails 4.0):

Configuración de la aplicación

 #config/application.rb config.exceptions_app = self.routes 

Rutas

 #config/routes.rb if Rails.env.production? get '404', to: 'application#page_not_found' get '422', to: 'application#server_error' get '500', to: 'application#server_error' end 

Controlador de aplicación

 #controllers/application_controller.rb def page_not_found respond_to do |format| format.html { render template: 'errors/not_found_error', layout: 'layouts/application', status: 404 } format.all { render nothing: true, status: 404 } end end def server_error respond_to do |format| format.html { render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500 } format.all { render nothing: true, status: 500} end end 

Diseño de errores (totalmente estático, solo para errores del servidor)

 #views/layouts/error.html.erb < !DOCTYPE html>   < %= action_name.titleize %> :: < %= site_name %> < %= csrf_meta_tags %>    
< %= yield %>

Vistas de error

 #views/errors/not_found_error.html.erb 

Sorry, this page has moved, or doesn't exist!

#views/errors/internal_server_error.html.erb
Error! We're sorry, but our server is experiencing problems :(

Aunque muchos prefieren el método de “rutas” por su simplicidad, no es eficiente ni modular. De hecho, si su aplicación tiene alguna apariencia de orientación a objetos, la descartará rápidamente como un hack.

Una forma mucho más contundente es usar un controlador personalizado para captar la excepción pura. De esta forma, puede construir el flujo de acuerdo con la estructura general de su aplicación:


2. Controlador

La otra opción es enrutar todas las solicitudes a un controlador.

Esto es infinitamente más poderoso ya que le permite tomar la solicitud (excepción) y pasarla a las vistas, mientras se administra en el back-end. Esto permitirá la posibilidad de guardarlo en la base de datos.

Esta esencia muestra cómo:

enter image description here

Significa que podemos conectarnos al middleware y pasar toda la solicitud a un controlador.

Si este controlador está respaldado por un modelo y vistas, podemos extraerlo en una gem (que es lo que hicimos). Si quería hacerlo manualmente, aquí le mostramos cómo hacerlo:

Config

La belleza de este método es que se engancha directamente en config.exceptions_app . Esto significa que cualquier excepción se puede manejar de forma nativa, lo que permite una mayor eficiencia. Para asegurarse de que esto funcione, debe colocar el siguiente código en config/application.rb ( exceptions_app solo funciona en production – el development muestra los errores):

 #config/application.rb config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) } 

Para probar, puede establecer las solicitudes “locales” en falso:

 #config/environments/development.rb config.consider_all_requests_local = false # true 

Controlador

El siguiente paso es agregar un controlador de exception . Si bien esto se puede manejar en application_controller , es mucho mejor extraerlo por sí mismo. Observe la llamada desde la application.rbExceptionController.action(:show) :

 #app/controllers/exception_controller.rb class ExceptionController < ApplicationController #Response respond_to :html, :xml, :json #Dependencies before_action :status #Layout layout :layout_status #################### # Action # #################### #Show def show respond_with status: @status end #################### # Dependencies # #################### protected #Info def status @exception = env['action_dispatch.exception'] @status = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code @response = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name] end #Format def details @details ||= {}.tap do |h| I18n.with_options scope: [:exception, :show, @response], exception_name: @exception.class.name, exception_message: @exception.message do |i18n| h[:name] = i18n.t "#{@exception.class.name.underscore}.title", default: i18n.t(:title, default: @exception.class.name) h[:message] = i18n.t "#{@exception.class.name.underscore}.description", default: i18n.t(:description, default: @exception.message) end end end helper_method :details #################### # Layout # #################### private #Layout def layout_status @status.to_s == "404" ? "application" : "error" end end 

-

Puntos de vista

Hay dos vistas para agregar para que funcione.

El primero es la vista de exception/show , y el segundo es el layouts/error . El primero es dar a exception_contoller#show a view, y el segundo para 500 errores internos del servidor.

 #app/views/exception/show.html.erb 

< %= details[:name] %>

< %= details[:message] %>

#app/views/layouts/error.html.erb (for 500 internal server errors) < !DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> Error
< %= yield %>

Conclusión

La excepción no importa tanto como el código de error .

Cuando Rails genera una excepción, asigna uno de los códigos de respuesta HTTP anteriores. Estos permiten que su navegador determine si la solicitud fue exitosa.

Cuando se trata de excepciones, debe asegurarse de que pueda manejar los errores 40* (que normalmente usarán el mismo diseño que el rest de su aplicación) y los errores 50* (que necesitarán su propio diseño).

En ambos casos, será mejor que use un controlador de exception separado, que le permitirá administrar la exception como un objeto.

Las páginas de error en la aplicación deben ser tan simples como sea posible. La misma recomendación se refiere a su procesamiento. Si su aplicación devuelve 500 código de respuesta HTTP, significa que las cosas ya han empeorado. Y existe la posibilidad de que no pueda mostrar la página de error y mostrarla al usuario.

Idealmente, las páginas de error deberían ser un HTML simple servido directamente por su servidor web sin tocar el servidor de la aplicación.

Hablando de la implementación de Rails de esta idea. Se basa en el uso de la canalización de activos para precomstackr las páginas estáticas HTML.

Primero agregue un nuevo tipo de activo (Rails> 4.1):

 # config/initializers/assets.rb Rails.application.config.assets.precompile += %w(404.html 500.html) Rails.application.config.assets.paths < < Rails.root.join('app/assets/html') Rails.application.config.assets.register_mime_type('text/html', '.html') 

Si el motor de plantillas está utilizando (por ejemplo, slim, haml), regístrelo a través del inicializador:

 # for Slim Rails.application.assets.register_engine('.slim', Slim::Template) # for Haml Rails.application.assets.register_engine('.haml', Tilt::HamlTemplate) 

Ahora está listo para crear páginas bonitas de error en el directorio app / assets / html utilizando su motor de plantilla favorito y los ayudantes de visualización incorporados en Rails.

Consejos para la producción

En la tubería de activos de producción agrega resumen a los activos comstackdos y almacena los archivos en la carpeta predeterminada (típicamente compartida / pública / activos en el servidor de producción). Puede usar capistrano para copiar las páginas de error a la raíz del servidor web:

 # config/deploy.rb # Capistrano 3 only namespace :deploy do desc 'Copy compiled error pages to public' task :copy_error_pages do on roles(:all) do %w(404 500).each do |page| page_glob = "#{current_path}/public/#{fetch(:assets_prefix)}/#{page}*.html" # copy newest asset asset_file = capture :ruby, %Q{-e "print Dir.glob('#{page_glob}').max_by { |file| File.mtime(file) }"} if asset_file execute :cp, "#{asset_file} #{current_path}/public/#{page}.html" else error "Error #{page} asset does not exist" end end end end after :finishing, :copy_error_pages end 

Y lo último. Indique al servidor web que use estos archivos para ciertos códigos de error HTTP (configuración nginx de muestra):

 error_page 500 502 503 504 /500.html; error_page 404 /404.html; 

Sprocket 3 Update

Para Sprocket 3 necesitas algo como esto (probado con Rails 5):

 # config/environments/production.rb config.assets.configure do |env| env.register_transformer 'text/slim', 'text/html', Slim::Template env.register_mime_type 'text/slim', extensions: ['.html'] env.register_engine '.slim', Slim::Template end # config/initializers/assets.rb Rails.application.config.assets.precompile += %w(404.html 500.html) Rails.application.config.assets.paths < < Rails.root.join('app/assets/html') 

Aquí está la última y rápida solución para mostrar la página 404_error personalizada.

  • Agregue las siguientes líneas en development.rb o production.rb según su env .

config.exceptions_app = self.routes

config.consider_all_requests_local = false

  • Eliminar todo rm public / {404,500,422} .html
  • Cree el archivo 404.html.erb en la carpeta estática de su proyecto de Rails. Puedes agregar tu html personalizado aquí (esto usará el diseño de tu aplicación, así que no te preocupes por el contenido del encabezado y el pie de página)