solucion not found error error-handling ruby-on-rails-4

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



error 404 not found (4)

📆 Actualización 2018 📆

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

exception_handler exception_handler

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 devolución de llamada:

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 :

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 https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L18-L22 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 devolución de llamada en config.exceptions_app . Esto se puede hacer en la aplicación o con una gema:

Observe cómo se usa el método de call : así es como funciona una devolución de llamada. 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 gema 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 :

exception_handler

exception_handler 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 exception_handler 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 here .

Gestión de excepciones de Rails

Si no estás interesado en la gema, 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 devolución de llamada:

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 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 devolución de llamada 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:

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> <html> <head> <title><%= action_name.titleize %> :: <%= site_name %></title> <%= csrf_meta_tags %> <style> body { background: #fff; font-family: Helvetica, Arial, Sans-Serif; font-size: 14px; } .error_container { display: block; margin: auto; margin: 10% auto 0 auto; width: 40%; } .error_container .error { display: block; text-align: center; } .error_container .error img { display: block; margin: 0 auto 25px auto; } .error_container .message strong { font-weight: bold; color: #f00; } </style> </head> <body> <div class="error_container"> <%= yield %> </div> </body> </html>

Vistas de error

#views/errors/not_found_error.html.erb <div class="error"> <h2>Sorry, this page has moved, or doesn''t exist!</h2> </div> #views/errors/internal_server_error.html.erb <div class="error"> <div class="message"> <strong>Error!</strong> We''re sorry, but our server is experiencing problems :( </div> </div>

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 gist muestra cómo:

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 gema (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.rb - ExceptionController.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 <h1><%= details[:name] %></h1> <p><%= details[:message] %></p> #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"> <html> <head> <title>Error</title> <style> html { height: 100%; background: #fff; } body { font-family: Helvetica, Arial, Sans-Serif; font-size: 14px; } .error_container { display: block; margin: auto; margin: 10% auto 0 auto; width: 40%; } .error_container .error { display: block; text-align: center; } .error_container .error img { display: block; margin: 0 auto 15px auto; } .error_container .message > * { display: block; } .error_container .message strong { font-weight: bold; color: #f00; } </style> </head> <body> <div class="error_container"><%= yield %></div> </body> </html>

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 resto 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.

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 < ApplicationController def sub_layout "left" end def error_404 render :status => 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!


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 rieles. 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)


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 precompilar 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 compilados 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'')