Rails 4: user error pages for 404, 500, and where does the 500 default error message come from?

Currently in production I am getting this text:

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

There is no html on this page.

Where is this code located? I don't have a public /500.html or anything for that matter.

On my routes, I:

  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 

How to always upload my custom errors? On some errors, it simply throws that the text from two lines comes from somewhere from the rails kernel, I want it to collect my user pages with errors every time! as? THX!

+54
ruby-on-rails-4 error-handling
Sep 30 '13 at
source share
4 answers

The error you encounter is thrown from

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

This means that the code, with the exception of which you receive salvation, throws exceptions. You can check your logs for text:

Error during failsafe response:

to determine what the exceptions actually come from, and thus solve your problem.

+43
Oct 13 '13 at 14:10
source share

NEW 2017

We created a gem for this - exception_handler

enter image description here enter image description here




How it works

All Rails exceptions are handled using the config.exceptions_app callback. This is assigned in the config/application.rb or config/environments/*.rb files - this should be a callback:

enter image description here

Whenever Rails accesses an error, it calls ShowExceptions middleware. This calls exception_app and sends the entire request (including exception ) to exceptions_app :

Middleware-Powered Exceptions

exceptions_app should provide an answer . If not, failsafe loads:

  # 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 

failsafe is stored as FAILSAFE_RESPONSE at the top of ShowExceptions .




Custom error pages

If you want to create your own error pages, you need to enter your own callback in config.exceptions_app . This can be done either in the application or using a gem:

enter image description here

Notice how the call method is used - this is how the callback works. Rails ( env ) is called when a request is received from the Internet; when an exception occurs, env is passed exceptions_app .

The quality of exception handling will depend on how you manage env . It is important; The self.routes link self.routes not take the environment forward.

It is best to handle exceptions with a separate controller. This allows you to process the request as if it were just another view providing access to layout and other components ( model / email ).

-

There are two ways to handle exceptions:

  • Redefining 404 Routes
  • Controller call

Our gem was designed around our controller - it is called every time an exception raised. This gives full control over the exclusion process, allowing 100% corporate identity :

enter image description here

ExceptionHandler now the leading personal graphical error editor for Rails.

Supported for over 3 years, this is the easiest and most powerful stone for Rails. It runs 100% on Rails 5 and has already been downloaded over 70,000 times.




Gem

The latest version 0.7.0 has the following updates:

  • Custom exceptions
  • Match exception (select which exceptions to handle)
  • Email Notifications
  • Backend models
  • Chain Integration 4+

You can read it here .




Rails Exception Exception

If you are not interested in the gem, let me explain the process:

All Rails exceptions are handled using the config.exceptions_app callback. This is assigned in the config/application.rb or config/environments/*.rb files - this should be a callback:

enter image description here

Whenever your application ShowExceptions an exception, ShowExceptions middleware is called. This middleware config.exceptions_app an exception in request and redirects it to the config.exceptions_app .

By default, config.exceptions_app points to routes. This is why Rails comes with 404.html , 500.html and 422.html in the public folder.

If you want to create custom exception pages, you need to override the config.exceptions_app - pass the erroneous request to the appropriate handler, be it controller or route :

[[middleware]]

Two ways to effectively manage: either send erroneous requests for routes, or call the controller.

The easiest and most common way is to redirect the request to routes; Unfortunately, this ignores the request and does not allow you to describe exceptions in detail.

The best way is to invoke a separate controller. This will allow you to transfer the entire request, allowing you to save it, send it by e-mail, or do a few more things.

-

400/500 Errors

Rails can only respond with valid HTTP errors .

While the application exception may be different, the return status code must be either 40x or 50x . It complies with the HTTP specification and describes here ↴

enter image description here

This means that no matter what exception handling solution you use / create, Rails should return 40x or 50x errors to the browser.

In other words, custom error pages have little to do with the type of exception β€” more so than how you catch and maintain the browser response.

By default, Rails does this with 404.html , 422.html and 500.html files in the public folder. If you want to handle the exception stream yourself, you need to delete these files and pass the erroneous requests to your own exceptions_app callback.

This can be done using routes or using the controller (which I will explain now):




1. Routes

The easiest way is to let routes handle it.

This method is bloated and requires several actions. It is also difficult to manage answers.

This tutorial explains:

enter image description here

This shows how to directly replace exceptions_app with routes:

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

Here is the code I have (Ruby 2.0.0, Rails 4.0):

Application configuration

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

Routes

 #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 

Application controller

 #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 

Error layout (fully static - only for server errors)

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

Error Views

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

While many prefer the β€œroutes” method for their simplicity, it is neither efficient nor modular. In fact, if your application has some sort of object orientation, you will quickly drop it as a hack.

A much louder way is to use a custom controller to detect a pure exception. This way you can build the thread according to the general structure of your application:




2. Controller

Another option is to redirect all requests to the controller.

This is infinitely more powerful, as it allows you to accept a request (exception) and pass it into views, managing it in the backend. This will save it to the database.

This gist shows how:

enter image description here

This means that we can connect to the middleware and pass the entire request to the controller.

If this controller is supported by the model and views, we can extract it into a gem (this is what we did). If you want to do it manually, here's how:

-

Config

The beauty of this method is that it is config.exceptions_app directly to config.exceptions_app . This means that any exception can be handled initially, providing greater efficiency. To make sure this works, you need to put the following code in config/application.rb ( exceptions_app only works in production - development shows errors):

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

To check, you can set the "local" queries to false:

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

-

controller

The next step is to add an exception controller. Although this can be handled in application_controller , it is much better to extract it yourself. Pay attention to the call from 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 

-

Views

There are two views you need to add in order to make this work.

The first is the exception/show view, and the second is layouts/error . First, give an exception_contoller#show representation, and secondly for 500 internal server errors.

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



Conclusion

An exception does not matter as an error code.

When Rails throws an exception, it assigns one of the above HTTP response codes. This allows your browser to determine if the request was successful.

When using exceptions, you need to make sure that you can handle 40* errors (which will usually use the same layout as the rest of your application) and 50* errors (which will require their own layout).

In both cases, it is best to use a separate exception controller, which allows you to control exception as an object.

+69
Oct 9 '13 at 17:43
source share

Error pages in the application should be as simple as possible. The same recommendation applies to their rendering. If your application returns 500 HTTP responses, this means that everything is no longer the case. And there is a possibility that you could not display the error page and display it to the user.

Ideally, the error pages should be plain HTML, which is served directly by your web server without entering the application server.

Speaking of the Rails implementation of this idea. It is based on the use of a resource pipeline to pre-compile static HTML pages.

First add a new asset type (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') 

If you are using a template engine (for example, slim, haml), register it through the initializer:

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

Now you are ready to create beautiful error pages in the app / assets / html directory using your favorite template engine and built-in Rails helpers.

Production Tips

In the pipeline of production assets, a digest is added to the compiled assets and stored in the default folder (usually shared / public / assets on the production server). You can use capistrano to copy error pages to the web server root:

 # 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 

And the last one. Tell the web server to use these files for specific HTTP error codes (nginx configuration example):

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

Asterisk Update

For Sprocket 3 you need something like this (tested with 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') 
+17
Aug 14 '14 at 8:40
source share

Here is the latest and quick fix for showing the 404_error user page.

  • Add below lines to development.rb or production.rb according to your env .

config.exceptions_app = self.routes

config.consider_all_requests_local = false

  • Delete all rm public / {404,500,422} .html
  • Create a 404.html.erb file in the static folder of your rails project. You can add your own html here (this will be the use of your application layout, so don't worry about the contents of the header and footer)
-3
Sep 26 '15 at 21:02
source share



All Articles