How to return correct HTTP error codes from Ruby on Rails application
Asked Answered
N

5

44

I have RoR 3.0 web application which is acting as an OAuth API provider. Now, in API I'd like to return correct HTTP error codes to the API consumer. How do I do this?

Here is example:

def destroy_oauth
    @item = Item.find(params[:id])
    if([email protected]? && @item.user_id == current_user.id)
        @item.destroy
        respond_to do |format|
                format.js
                format.xml
        end
    else
        raise ActionController::RoutingError.new('Forbidden')
    end
end

So, in case of error I'm trying to return Forbidden 403 code. Still, when running this I'm getting always 404 Not Found returned. How do I return the correct code?

Or is this somehow webserver configurable thing?

Nicolella answered 3/2, 2012 at 14:29 Comment(0)
B
50

You should render page with correct status.

render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false)
Blodgett answered 3/2, 2012 at 14:33 Comment(3)
Should I do this also for OAuth API xml responses?Nicolella
As a side-note here, specifying the .html extension like this is deprecated (see discussion at github.com/rails/rails/issues/7288), so we should use render(:file => File.join(Rails.root, 'public/403'), :formats => [:html], :status => 403, :layout => false) (note the way I've specified the format).Christianechristiania
... or even render(:file => Rails.root.join('public', '403'), :formats => [:html], :status => 403, :layout => false).Christianechristiania
W
69

When you're just giving a status code and there is no body, a convenient way is

head 403

This method also accepts the symbolic names for status codes, such as

head :forbidden
Wolof answered 3/2, 2012 at 15:2 Comment(1)
Oh, that could be what I need. Will try.Nicolella
B
50

You should render page with correct status.

render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false)
Blodgett answered 3/2, 2012 at 14:33 Comment(3)
Should I do this also for OAuth API xml responses?Nicolella
As a side-note here, specifying the .html extension like this is deprecated (see discussion at github.com/rails/rails/issues/7288), so we should use render(:file => File.join(Rails.root, 'public/403'), :formats => [:html], :status => 403, :layout => false) (note the way I've specified the format).Christianechristiania
... or even render(:file => Rails.root.join('public', '403'), :formats => [:html], :status => 403, :layout => false).Christianechristiania
N
18

According to ActionController::Head docs just use this pattern in actions

  return head([status]) if/unless [some condition here]

Example:

  return head(:gone) if @record.deleted?
  return head(:forbidden) unless @user.owns?(@record)

return is used to make sure that no remaining code in the action will be run.

Nightingale answered 8/2, 2014 at 21:0 Comment(2)
Is there any difference between head :forbidden && return and return head :forbidden? In my testing,I had to use the latter to return early from controller. With the former, I got 204 returned.Debarath
@Debarath Ahhhh that's why I was getting a 204 No Content returned, I couldn't understand why head :forbidden && return didn't work, while head(:forbidden) && return didAmpliate
D
3

I think you have two problems here: first is that your @item = Item.find(params[:id]) line is raising 404 and execution never gets to where intended (if statement). Second is that you are raising exceptions and never catch them. Try:

def destroy_oauth
   begin
     @item = Item.find(params[:id])
     if([email protected]? && @item.user_id == current_user.id)
       @item.destroy
       respond_to do |format|
          format.js
          format.xml
       end
     else
       raise ActionController::RoutingError.new('Forbidden')
     end
   rescue ActiveRecord::ResourceNotFound
     redirect_to :action => 'not_found', :status => 404 # do whatever you want here
   rescue ActionController::RoutingError
     redirect_to :action => 'forbidden', :status => 403 # do whatever you want here
   end
 end

Something along those lines, but you also mentioned that you are building the API, so when you are rescuing the error, you may want to render xml error info. Something like:

# in application_controller.rb
rescue_from ActionController::RoutingError, :with => :render_forbidden_error

private

def render_forbidden_error(e)
  render :status => e.status, :xml => e
end

Good luck. Udachi.

Dvandva answered 3/2, 2012 at 15:38 Comment(0)
F
1

well, you can use

:status =>500

But, In default Rails take care of the error type rendering itself.

Errors default pages are in the public directory. 500.html,404.html etc..

For more information on :status , how to use it click here

Firehouse answered 3/2, 2012 at 14:59 Comment(2)
Yes, I probably have to play with these :status parameters. Thanks!Nicolella
Well, this is true. However, if I am working on an API in rails, I would need to explicitly mentioned the status codeDibri

© 2022 - 2024 — McMap. All rights reserved.