I am using rocketpant for rest API implementation. From this doc I can see rocketpant has considered error handling in controller nicely.
However, it seems like it does not consider routing error, so if the requested url has no route defined, instead of returning json error, it render back html 404 page, which is not correct for API design.
Do I miss something? Or is there a way to wrap 404 routing error and render a json error message?
I found the solution.
Step1: catch all unmatched routes in the last line of routing file.
match '*a', to: 'error#routing', via: [:get, :post, :put, :patch]
Step2: define a error controller and routing method as custom error handler. throw rocketpant built in :not_found error
module Api
module V1
class ErrorController < ApiController
def routing
error! :not_found # throw not found error, let rocketpant handle error message rendering
end
end
end
end
Related
This question already has answers here:
rescue_from ActionController::RoutingError doesn't work
(2 answers)
Closed last month.
In my Rails 7 API only mode, I'm trying to catch case when someone is trying to make a request into api/v1/mandate instead of api/v1/mandates (classic ActionController::RoutingError case). In such case it will render the 404 error with some build in stuff as JSON response. I want to customize that JSON error answer inside my BaseController. I was trying:
#base_controller.rb
module Api
module V1
class BaseController < ActionController::API
rescue_from ActionController::RoutingError, with: :render_not_found_error
private
def render_not_found_error(exception)
render json: { error: true, message: exception.message }, status: :not_found
end
end
end
end
I've also have:
#config/environments/development.rb
config.consider_all_requests_local = true
But nothing changed, still doesn't get my JSON error.
You can't rescue routing errors in a controller because the exceptions occur in the routing layer, not the controller.
For missing routes, you need a "catch-all" route that can capture them and then forward that to a controller which performs your "exception" handling.
In practice this often shakes out as a NotFoundController with different actions depending on the type of route being handled.
Can you help me in this. I want to add 404 error page to my website.
I tried many ways but nothing works for me.
Here is one of them.
ApplicationController
unless ActionController::Base.consider_all_requests_local
rescue_from Exception, :with => :render_404
end
private
def render_404
render :template => 'error_pages/404', :layout => false, :status => :not_found
end
Then set up error_pages/404.html
Can anyone fix this?
Rescuing 404 (usually originating from ActiveRecord::RecordNotFound) from your controller will not always help, because there're also routing errors (ActionController::RoutingError)
To present exceptions to user rails calls config.exceptions_app, which defaults to ActionDispatch::PublicExceptions.new(Rails.public_path) and just renders public/404.html etc.
For most cases it's enough to place your error pages there, but if you absolutely must render something dynamic - you can override the exceptions app.
One common hack is to route exceptions back into your main routes via config.exceptions_app = self.routes and adding regular routes to handle them:
get '/404', to: 'errors#not_found'
get '/422', to: 'errors#unacceptable'
get '/500', to: 'errors#server_error'
But beware, that if there's already an exception - you may get an exception loop, so it's better to have separate error handling app/engine
If you are simply wanting custom static error pages, you can change the HTML inside of public/404.html, public/422.html and public/500.html.
I'm trying to get my app to redirect to a custom route when it encounters the error:
Twitter::Error::TooManyRequests
However, I'm having difficulty for some reason and i keep getting this error:
This webpage has a redirect loop
Here's my controller:
#app/controllers/tweets_controller.rb
rescue_from Twitter::Error::TooManyRequests, with: :too_many_requests
def too_many_requests
redirect_to too_many_requests_path
end
Here's my routes:
#config/routes.rb
get "/too_many_requests", to: "tweets#too_many_requests", as: :too_many_requests
I have a view within app/views/tweets named too_many_requests.html.erb
I know i must be doing something incorrectly but can someone help?
Thanks
Unless I'm missing something, it looks like you redirect the action to itself:
def too_many_requests
# Error handling.....
# You should redirect this elsewhere
redirect_to some_other_path
end
I'm trying to get a rails app running with the node.js framework batman.js via the batman-rails gem.
When I'm responding with json in my rails controller, I get a 406 error everytime, and I don't know why. Here's my controller:
respond_to :json
def index
respond_with Sample.all
end
This gives me a 406 no matter what. I do not think this is related to batman, but rails itself. But for good measure, here's my batman code:
index: (params) ->
TopNavTemplate.Sample.load (err) -> throw err if err
#set 'samples', TopNavTemplate.Sample.get('all')
And then my index.html.erb file simply says 'index', it isn't really doing anything with batman yet.
There is a lot of 406 JSON related questions, I haven't really been able to apply them yet to my situation. Is there something I'm doing wrong to make rails respond_with JSON?
Alright, I made a very simple app to check out your situation:
SamplesController.rb
class SamplesController < ApplicationController
respond_to :json
def show
respond_with Sample.find(params[:id])
end
def index
respond_with Sample.all
end
end
When I visited /samples.json and samples/1.json, it worked as intended. However, when I went to /samples and /samples/1 (no .json extension), I got a 406 error.
In order to have the URL's work without the .json extension, you need to modify your config/routes.rb file as follows:
resources :samples, defaults: {format: :json}
Otherwise, the Rails application will attempt to respond to the request with an HTML response.
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