Alex Rivera | Logout

PUT requests in rails don't update the status on respond_with calls

Asked 2013-01-24T08:21:01.437
11

Given the following controller in rails:

class AccountsController < ApplicationController
    respond_to :json, :xml
    def update
        @account = Account.where(uuid: params[:id]).first
        unless @account.nil?
            if @account.update_attributes params[:account]
                respond_with @account, location: account_url(@account)
            else
                respond_with error_hash, status: :unprocessable_entity, root: :error, location: api_account_url(@account)
            end
        else
            respond_with error_hash, status: :not_found, root: :error, location: accounts_url
        end
    end

    def error_hash
        { :example => "Example for this question", :parameter => 42 }
    end
end

I would expect a PUT request to /accounts/update/ to do the following

  1. If the id exists, and the update_attributes call succeeds, deliver a 204 (No Content) success message. (I have it set to return @account, which would be nice, but no big deal. 204 is fine here.)
  2. If the id exists, but the data is bad, deliver a 422 (Unprocessable Entity) error message, along with the xml/json to represent the error.
  3. If the id does not exist, deliver a 404 (Not Found) error message, along with the xml/json to represent the error.

What actually happens is:

  1. Deliver a 204 with no body.
  2. Deliver a 204 with no body.
  3. Deliver a 204 with no body.

Why is it that it ignores both my status and my body? I've had a similar setup for GET requests that work out just fine (correct status, correct body).

Example CURL request (for an ID that does not exist):

PUT request

curl -i --header "Accept: application/xml" --header "Content-type: application/json" -X PUT -d '{"name": "whoop"}' http://localhost:3000/api/accounts/3d2cc5d06
Edit
Report

1 Answer

3

Did you see ActionController::Responder class ? Here are some methods to think about

 # All other formats follow the procedure below. First we try to render a
    # template, if the template is not available, we verify if the resource
    # responds to :to_format and display it.
    #
    def to_format
      if get? || !has_errors? || response_overridden?
        default_render
      else
        display_errors
      end
    rescue ActionView::MissingTemplate => e
      api_behavior(e)
    end

and

def api_behavior(error)
      raise error unless resourceful?

      if get?
        display resource
      elsif post?
        display resource, :status => :created, :location => api_location
      else
        head :no_content
      end
    end

As you can see api_behavior works for post and get methods but not for put . If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request.

head :no_content is what you get.

So reason of this is that rails doesn't understand what are you trying to do. Rails thinks there is no error when you use respond_with in this case.(it's not a bug you just shouldn't use it that way)

I think respond_to is what you need.

answered 2013-06-13T21:22:37.887

Your Answer