Handle error page in rails - ruby-on-rails

for ex: I type the url: localhost:3000/hoasung01 then it throws a message:
Unknown action
The action 'hoasung01' could not be found for StaticController
And in console:
AbstractController::ActionNotFound (The action 'hoasung01' could not be found for StaticController):
I spent many hours to research and try some ways:
rescue_from AbstractController::ActionNotFound, with: :page_not_found
#(ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound)
protected
def page_not_found
render 'layouts/404.html', :status => 403, :layout => false
end
but it does not render 404.html page. ( I use rails 4.1.5 )

In development you are shown a special error page to help you figure out errors. I you want to test that 404 and 500s work look at this blog post: http://thepugautomatic.com/2014/08/404-with-rails-4/
The section Verify in development says t config/environments/development.rb and add the following line:
config.consider_all_requests_local = false

Related

How to controller the error message displayed in Rails 5?

I'm working to build a Rails 5 app, not in API mode, but for an API.
One of my APIs is broken. When I load the url in the browser: http://localhost:4300/api/v1/skills.json
The browser is returning just:
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.
Given I'm in development mode, how can I get Rails to output more helpful information like in previous versions of Rails?
Add to application.rb the following line:
config.exceptions_app = self.routes
so you can render custom errors adding in routes:
Rails.application.routes.draw do
get "/404" => "errors#not_found"
get "/500" => "errors#exception"
post "/500" => "errors#exception"
get "/400" => "errors#exception"
post "/400" => "errors#exception"
for example I display:
class ErrorsController < ApplicationController
def not_found
render :json => {:error => "not-found"}.to_json, :status => 404
end
def exception
render json: {
error: {
message: env["action_dispatch.exception"].to_s,
detail: env["action_dispatch.exception"]
}
},
:status => 500
end
end

DRYing up Rails error page

I have a Rails app which is part of a larger web site. I'd like to have a single 404 error page that's used consistently across the site. Currently I have a static page that's part of the landing site, which for historical reasons is served from the public section of the Rails app:
public/landing/404.html
Now I'd like my Rails app to serve that page in case of a 404 error. The approach I've tried is adapted from this blog post:
config/application.rb:
config.exceptions_app = self.routes
config/routes.rb:
match '/404', to: redirect('/landing/404'), via: :all
This appears to work, in that a 404 error will deliver the landing/404.html page to the user agent that made the request. However, it delivers the page with status 200, because the server successfully redirected to the static page. So, not web standards compliant (and not very RESTful!).
My question is: can I serve the static page but with a 404 response code? Or is there a better way to DRY up my error page config?
You can do the following in application_controller.rb:
unless Rails.application.config.consider_all_requests_local
rescue_from ActiveRecord::RecordNotFound, with: :render_404
end
Here, as you can guess, you can catch any type of exceptions you are interested in (ActionController::RoutingError, ActionController::UnknownController, or generally Exception), and it will only redirect in production, whereas in development it will show real exception.
def render_404
respond_to do |format|
format.html { render( layout: false, file: Rails.root.join( 'public', 'landing', status.to_s ), status: status ) }
format.all { render nothing: true, status: 404 }
end
end
and leave routes.rb as it is.

How do I get exceptions to render a plain text version of the Rails development error page?

I am using rails-api to build an API with no web interface. When I get errors in development, I'd love to see just the error message and stacktrace in plain text without all of the HTML wrapping. How do I override the global exception handling so it renders the stacktrace in development mode in plain text/JSON, and a generic error message in production?
I would suggest that including the stack trace in production code is probably not a good idea from a security stand point.
Here is how I would do it:
render :json => {message:exception.message, stack_trace: exception.stacktrace}
I hope this helps.
After Sam's clarification I can add:
In your base controller for your API (probably ApplicationController):
class ApplicationController < ActionController::Base
...
rescue_from Exception do |exception|
error = {message:exception.message}
error[:stack_trace] = exception.stacktrace if Rails.env.development?
render :json => error
end
...
end
Caveat: You may not want to rescue from every single exception in this way but this is how you'd do it if you did.
Some improvements over #donleyp answer to get a clean trace in Rails 3.2 and output generic error info in production:
class ApplicationController < ActionController::API
...
rescue_from Exception do |exception|
if Rails.env.development?
error = {message:exception.message}
error[:application_trace] = Rails.backtrace_cleaner.clean(exception.backtrace)
error[:full_trace] = exception.backtrace
render :json => error
else
render :text => "Internal server error.", :status => 500
end
end
...
end

Catch Unknown action in Rails 3 for custom 404

I want to catch unknown action error in Rails 3, that shows "Unknown Action" error on development and the 404.html on production. I tried putting this rescue_from handler on my ApplicationController (and also on an actual controller, just in case) but I still see the ugly error.
I have custom stuff on the 404, and it can't be plain .html file.
My route:
match '/user/:id/:action', controller: 'users'
The URL I'm accessing: /user/elado/xxx
The rescue_from code:
rescue_from AbstractController::ActionNotFound, :with => :action_not_found
def action_not_found
render text: "action_not_found"
end
The error in the browser:
Unknown action
The action 'xxx' could not be found for UsersController
And in the console:
Started GET "/user/elado/xxx" for 127.0.0.1 at 2011-09-07 19:16:27 -0700
AbstractController::ActionNotFound (The action 'xxx' could not be found for UsersController):
Tried also rescue_from ActionController::UnknownAction.
Any suggestions?
Thanks!
rescue_from was slightly broken when Rails 3 came out (still broken in 3.1 too). Basically you can't:
rescue_from ActionController::RoutingError
anymore. See here.
The solution, for now, is what hamiltop recommends. Use a catch all route that goes to your "routing error" route. Make sure you put it at the end of your config\routes.rb file so it is processed last.
# Any routes that aren't defined above here go to the 404
match "*a", :to => "application#routing_error"
def routing_error
render "404", :status => 404
end
Note: This method has one major drawback. If you use an engine such as Jammit or Devise the catch all will route will make Rails ignore the engine's routes.
If you aren't using an engine that has it's own routes then you should be fine.
However, if you do use an engine that defines its own routes see #arikfr's answer.
Using a catch all route to handle 404 erros (as #Seth Jackson suggested) has one major drawback: if you use any Rails engines that define their own routes (such as Jammit) their routes will be ignored.
Better and more compliant solution would be to use a Rack middleware that will catch 404 errors. In one of my projects, I've implemented such Rack middleware that reports these errors to Hoptoad. I've based my implementation on this one: https://github.com/vidibus/vidibus-routing_error, but instead of invoking my Rails app again to handle the 404 error, I do it in the Rack middleware and let nginx to show the 404 page.
If you really want to rescue AbstractController::ActionNotFound in a controller, you can try something like this:
class UsersController < ApplicationController
private
def process(action, *args)
super
rescue AbstractController::ActionNotFound
respond_to do |format|
format.html { render :404, status: :not_found }
format.all { render nothing: true, status: :not_found }
end
end
public
# actions must not be private
end
This overrides the process method of AbstractController::Base that raises AbstractController::ActionNotFound (see source).
Have you tried a catch all route?
http://railscasts.com/episodes/46-catch-all-route
The wildcard route that you are current using is a Bad Idea(tm).
I would recommend defining the routes you care about, and then doing a catchall as the last line of routes.rb (routes defined first in routes.rb trump later definitions). Then you can render whatever page you want (and specify the 404 status code).
Edit: If you really want to use your current approach... (although this seems like it could be deprecated)
def rescue_action(exception)
case exception
when ActionNotFound, UnknownAction then
# Handle these exceptions here
else
super
end
end

How do you issue a 404 response from a rails controller action?

What's the preferred way to issue a 404 response from a rails controller action?
This seems good...
# Rails 2 and below
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
# Rails 3 and up
render :file => "#{Rails.root}/public/404.html", :status => 404
You can also
raise ActiveRecord::RecordNotFound
exception.
The following way was the best for me:
raise ActionController::RoutingError.new('Not Found')
or just
raise ActionController::RoutingError, 'Not Found'
Or there are some other solutions:
How to redirect to a 404 in Rails?
Reference:
render :file => '/path/to/some/filenotfound.rhtml',
status => 404, :layout => true
In the ApplicationController define a method like:
def render_404
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
end
In Rails 5 you can use
head 404
This will set the response code of your action to 404. If you immediately return after this from your action method, your action will respond 404 with no actual body. This may be useful when you're developing API endpoints, but with end user HTTP requests you'll likely prefer some visible body to inform the user about the error.
If you wanted to issue a status code on a redirect (e.g. 302), you could put this in routes.rb:
get "/somewhere", to: redirect("/somewhere_else", status: 302).
However, this would only work for redirection, not straight up loading a page.

Resources