Skip to content

Commit

Permalink
Respond with 422 for non-GET HTML requests with errors
Browse files Browse the repository at this point in the history
When responding to a non-GET HTML request that had errors on the
resource, responders would not set any status, which means Rails would
default to `200 OK`. This has been long working like this but also a
long discussion in responders to change that behavior, to be more inline
with how other types of requests (like JSON/XML) handle responses by
setting the HTTP status to 422 Unprocessable Entity when responding with
an error.

More recently with the Turbo/Hotwire library, they've added support to
render form responses as errors with 4xx statuses [1], so it makes even
more sense for responders to make the move here.

This change now makes responders use the 422 Unprocessable Entity status
in such cases when the resource has errors, and should work more out of
the box with Turbo/Hotwire.

Please note that this is a possible breaking change if you're relying on
the previous status to trigger any behavior for errors in your app.

[1] hotwired/turbo#39
  • Loading branch information
carlosantoniodasilva committed Jan 16, 2021
1 parent 1c4f548 commit 8f4de70
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 16 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## Unreleased

* Add support for Ruby 3.0, drop support for Ruby < 2.5
* Add support for Rails 6.1, drop support for Rails < 5.2
* Move CI to GitHub Actions
* Responding to an `HTML` request that has errors on the resource now sets the status to `422 Unprocessable Entity`. (instead of the default of `200 OK`.) This makes it more consistent with other statuses more commonly used in APIs (JSON/XML for example), and works by default with Turbo/Hotwire. Note that this change may break your application if you're relying on the previous 2xx status to handle error cases.
* Add support for Ruby 3.0, drop support for Ruby < 2.5.
* Add support for Rails 6.1, drop support for Rails < 5.2.
* Move CI to GitHub Actions.

## 3.0.1

Expand Down
6 changes: 3 additions & 3 deletions lib/action_controller/responder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def navigation_behavior(error)
if get?
raise error
elsif has_errors? && default_action
render rendering_options
render error_rendering_options
else
redirect_to navigation_location
end
Expand Down Expand Up @@ -300,11 +300,11 @@ def response_overridden?
@default_response.present?
end

def rendering_options
def error_rendering_options
if options[:render]
options[:render]
else
{ action: default_action }
{ action: default_action, status: :unprocessable_entity }
end
end
end
Expand Down
20 changes: 10 additions & 10 deletions test/action_controller/respond_with_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,13 @@ def test_using_resource_for_post_with_html_redirects_on_success
end
end

def test_using_resource_for_post_with_html_rerender_on_failure
def test_using_resource_for_post_with_html_rerender_and_yields_unprocessable_entity_on_failure
with_test_route_set do
errors = { name: :invalid }
Customer.any_instance.stubs(:errors).returns(errors)
post :using_resource
assert_equal "text/html", @response.media_type
assert_equal 200, @response.status
assert_equal 422, @response.status
assert_equal "New world!\n", @response.body
assert_nil @response.location
end
Expand Down Expand Up @@ -305,26 +305,26 @@ def test_using_resource_for_patch_with_html_redirects_on_success
end
end

def test_using_resource_for_patch_with_html_rerender_on_failure
def test_using_resource_for_patch_with_html_rerender_and_yields_unprocessable_entity_on_failure
with_test_route_set do
errors = { name: :invalid }
Customer.any_instance.stubs(:errors).returns(errors)
patch :using_resource
assert_equal "text/html", @response.media_type
assert_equal 200, @response.status
assert_equal 422, @response.status
assert_equal "Edit world!\n", @response.body
assert_nil @response.location
end
end

def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override
def test_using_resource_for_patch_with_html_rerender_and_yields_unprocessable_entity_on_failure_even_on_method_override
with_test_route_set do
errors = { name: :invalid }
Customer.any_instance.stubs(:errors).returns(errors)
@request.env["rack.methodoverride.original_method"] = "POST"
patch :using_resource
assert_equal "text/html", @response.media_type
assert_equal 200, @response.status
assert_equal 422, @response.status
assert_equal "Edit world!\n", @response.body
assert_nil @response.location
end
Expand All @@ -340,27 +340,27 @@ def test_using_resource_for_put_with_html_redirects_on_success
end
end

def test_using_resource_for_put_with_html_rerender_on_failure
def test_using_resource_for_put_with_html_rerender_and_yields_unprocessable_entity_on_failure
with_test_route_set do
errors = { name: :invalid }
Customer.any_instance.stubs(:errors).returns(errors)
put :using_resource

assert_equal "text/html", @response.media_type
assert_equal 200, @response.status
assert_equal 422, @response.status
assert_equal "Edit world!\n", @response.body
assert_nil @response.location
end
end

def test_using_resource_for_put_with_html_rerender_on_failure_even_on_method_override
def test_using_resource_for_put_with_html_rerender_and_yields_unprocessable_entity_on_failure_even_on_method_override
with_test_route_set do
errors = { name: :invalid }
Customer.any_instance.stubs(:errors).returns(errors)
@request.env["rack.methodoverride.original_method"] = "POST"
put :using_resource
assert_equal "text/html", @response.media_type
assert_equal 200, @response.status
assert_equal 422, @response.status
assert_equal "Edit world!\n", @response.body
assert_nil @response.location
end
Expand Down

0 comments on commit 8f4de70

Please sign in to comment.