Correct Rails path to display error with jbuilder

I am looking to display an error message in a jbuilder view. For example, one route that could be could be:

/foos/:id/bars

If :id provided by the user does not exist or is invalid, I would like to be able to display the error message accordingly in my index.json.builder file.

Using Rails, what's the best way to do this? The controller may have something like:

 def index @bar = Bar.where(:foo_id => params[:id]) end 

In this case, params[:id] may be nil , or this object may not exist. I'm not sure that the best thing that can be done here is to process it in the controller and explicitly display error.json.builder or process it in the index.json.builder view index.json.builder . What is the correct way to do this, and if it is in index.json.builder , is there params[:id] to check there? I know what I can see if @bar.nil? but not sure otherwise?

+7
source share
2 answers

I would do index.json.builder or just inline json with :error => 'not found' And don't forget to set proper HTTP :status => 404

Thus, the result may look like this:

 render :json => { :error => 'not found' }, :status => 422 if @bar.nil? 
+4
source

I think you meant the show, since the index is valid for lists / collections. And you should get .first from where, otherwise you just have a relationship, right? Then use .first! to increase the error, because the Rails Rack middleware in Rails 4 public_exceptions will be processed mainly, for example,

 def show # need to do to_s on params value if affected by security issue CVE-2013-1854 @bar = Bar.where(:foo_id => params[:id].to_s).first! end 

You can also use @bar = Bar.find(params[:id]) , but this is deprecated and will be removed in Rails 4.1, after which you will need to add the gem 'activerecord-deprecated_finders' to your Gemfile to use.

For the index, you probably need @bars = Bar.all . If for some reason you want to filter and do not want to have visibility, etc., you can use @bars = Bar.where(...).to_a or similar.

Rails 4: Basic Rack Exception Handling Automatic

While the request is triggering an error, Rails 4 should be able to return part of the error message for any supported format where to_(format) can be caused by a hash (e.g. json, xml, etc.).

To find out why, look at the Rails Rack public_exceptions middleware.

If it is html, it will try to read in the linked file from the shared directory in Rails for the status code (e.g. 500.html for server / HTTP error 500).

If this is some other format, it will try to make to_(the format) in the hash: { :status => status, :error => exception.message } to_(the format) { :status => status, :error => exception.message } . To find out how it works, go to the Rails console:

 $ rails c ... 1.9.3p392 :001 > {status: 500, error: "herro shraggy!"}.to_xml => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <status type=\"integer\">500</status>\n <error>herro shraggy!</error>\n</hash>\n" 1.9.3p392 :002 > {status: 500, error: "herro shraggy!"}.to_json => "{\"status\":500,\"error\":\"herro shraggy!\"}" 

In the middleware, you will see the X-Cascade header in the code and in various places related to handling Rails exceptions in Rack. Per this answer , the X-Cascade header is set to pass to tell Rack to try other routes to find the resource.

Rails 3.2.x: can handle rack exceptions

In Rails 3.2.x, this code is for to_(format) for the response body, etc. not located in public_exceptions.rb . It only processes the html format.

Perhaps you could try replacing the old middleware with a newer version using a patch.

If you prefer Rack to handle your error in a more specific way without a patch, see No. 3 in a post by Josรฉ Valim, " My five favorites are" hidden "features in Rails 3.2 ."

In this and as another answer is also mentioned, you can use config.exceptions_app = self.routes . Then, using routes that point to the user controller, you can handle errors from any controller, like any other request. Note the bit about config.consider_all_requests_local = false in config/environments/development.rb .

You do not need to use routes to use exceptions_app . Although this can be a little intimidating, it's just proc / lambda that takes a hash and returns an array whose format is [http_status_code_number, {headers hash...}, ['the response body']] . For example, you should be able to do this in your Rails 3.2.x configuration to make it handle errors like Rails 4.0 (this last public_exceptions middleware is minimized):

 config.exceptions_app = lambda do |env| exception = env["action_dispatch.exception"] status = env["PATH_INFO"][1..-1] request = ActionDispatch::Request.new(env) content_type = request.formats.first body = { :status => status, :error => exception.message } format = content_type && "to_#{content_type.to_sym}" if format && body.respond_to?(format) formatted_body = body.public_send(format) [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [formatted_body]] else found = false path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path)) if found || File.exist?(path) [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [File.read(path)]] else [404, { "X-Cascade" => "pass" }, []] end end end 

Note. For any problem with this processing, the fail-safe implementation is in ActionDispatch::ShowExceptions here .

Rails 3 and 4: Handling Some Exceptions in the Rails Controller

If you prefer rendering errors in the controller itself, you can do:

 def show respond_with @bar = Bar.where(:foo_id => params[:id].to_s).first! rescue ActiveRecord::RecordNotFound => e respond_to do |format| format.json => { :error => e.message }, :status => 404 end end 

But you do not need to make mistakes. You can also do:

 def show @bar = Bar.where(:foo_id => params[:id].to_s).first if @bar respond_with @bar else respond_to do |format| format.json => { :error => "Couldn't find Bar with id=#{params[:id]}" }, :status => 404 end end end 

You can also use rescue_from , for example. in your controller or ApplicationController etc:

 rescue_from ActiveRecord::RecordNotFound, with: :not_found def not_found(exception) respond_to do |format| format.json => { :error => e.message }, :status => 404 end end 

or

 rescue_from ActiveRecord::RecordNotFound do |exception| respond_to do |format| format.json => { :error => e.message }, :status => 404 end end 

Although some common errors can be handled in the controller, if you made a mistake regarding missing routes, etc. formatted in json, etc., they need to be handled in the Rack middleware.

+4
source

All Articles