1

How does Rails calculates the response codes for controller actions?

Given the following controller action:

def update
  respond_to do |format|
    if @user.update(user_params)
      format.html { redirect_to @user, notice: 'User was successfully updated.' }
      format.json { head :no_content }
    else
      format.html { render action: 'show' }
      format.json { render json: @user.errors, status: :unprocessable_entity }
    end
  end
end

(I'm using the same view to show and edit the record)

With this positive test:

test "should update basic user information" do
  user = users(:jon)
  user.first_name="Jonas"
  put :update, :id => user.id, :merchant_user =>user.attributes
  assert_response :found
  user = Merchant::User.find(user.id)
  assert user.first_name == "Jonas", "Should update basic user information"
end

And a negative test is like this:

test "should not update user email for an existing email" do
  user = users(:jon)
  original_user_email = user.email
  existing_user_email = users(:doe)
  user.email=existing_user_email.email
  put :update, :id => user.id, :merchant_user =>user.attributes
  assert_response :success
  user = Merchant::User.find(user.id)
  assert user.email == original_user_email, "Should not update email for an exising one"
end

Updating the record successfully, causes a 302 response code, which I assume that rails defaults to 302 for the GET resource/:ID. A failure to update the record cause a 200 OK.

How are these response codes being calculated and how can I override them?

Thanks

sscirrus
  • 55,407
  • 41
  • 135
  • 228
Joao Pereira
  • 2,454
  • 4
  • 27
  • 35
  • 1
    There are diffrent codes returned for HTML and JSON requests. I think 200 OK is returned on saving error to ensure comatibility with browser behaviour. – Mike Szyndel Jul 13 '13 at 08:53

2 Answers2

5

See inline comments below

if @user.update(user_params)
  format.html { redirect_to @user, notice: 'User was successfully updated.' }
  # 302, the save was successful but now redirecting to the show page for updated user
  # The redirection happens as a “302 Found” header unless otherwise specified.

  format.json { head :no_content }
  # 204, successful update, but don't send any data back via json

else
  format.html { render action: 'show' }
  # 200, standard HTTP success, note this is a browser call that renders 
  # the form again where you would show validation errors to the user

  format.json { render json: @user.errors, status: :unprocessable_entity }
  # 422, http 'Unprocessable Entity', validation errors exist, sends back the validation errors in json

end

if you look at format.json { render json: @user.errors, status: :unprocessable_entity } it uses the status option on render to be explicit about the HTTP status code so you can do render action: 'show', status: 422 or render action: 'show', status: :unprocessable_entity if you wanted to (you probably don't) - and render defaults to 200 Ok (rails uses a symbol :success to alias :ok as well

see also:

see Getting access to :not_found, :internal_server_error etc. in Rails 3 in your console Rack::Utils::HTTP_STATUS_CODES to view all status codes (the values are symbols in rails), i.e. Unprocessable Entity is :unprocessable_entity

Community
  • 1
  • 1
house9
  • 20,359
  • 8
  • 55
  • 61
1
  1. I highly doubt your code is working as you use update instead of update_attributes in controller. update is a private method in ActiveRecord::Callback which is not for consuming publicly. Thanks for Michael's comment pointing out update is a replacement of update_attributes in Rails 4, though not mentioned in question.

  2. Testing response is unnecessary and it's even more unnecessary to break the conventional response code. Instead, check the effect taken on ActiveRecord, and the response body or path.

Billy Chan
  • 24,625
  • 4
  • 52
  • 68