Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respond with 422 Unprocessable Entity for non-GET HTML/JS requests with errors #223

Merged
merged 3 commits into from Jan 29, 2021

Conversation

carlosantoniodasilva
Copy link
Member

@carlosantoniodasilva carlosantoniodasilva commented Jan 16, 2021

When responding to a non-GET HTML/JS 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

Closes #159.

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
Much like HTML responses, JS will respond with a 422 Unprocessable
Entity for non-GET requests if the given resource contains errors, so it
is consistent with all other formats going forward.

Closes #159.
@carlosantoniodasilva carlosantoniodasilva changed the title Respond with 422 Unprocessable Entity for HTML requests with errors Respond with 422 for non-GET HTML requests with errors Jan 16, 2021
@carlosantoniodasilva carlosantoniodasilva changed the title Respond with 422 for non-GET HTML requests with errors Respond with 422 Unprocessable Entity for non-GET HTML/JS requests with errors Jan 16, 2021
@ghiculescu
Copy link

ghiculescu commented Jan 18, 2021

Just tested this out, in a website + app running Turbo (+ native).

  • RegistrationsController#(new|create) -> ✅ . Creating an account works fine, and errors are rendered correctly (with a 422 status) for an invalid signup attempt.
  • RegistrationsController#(edit|update) -> ✅ . Can save user details, and invalid save attempts are rendered correctly (with a 422 status).
  • PasswordsController#(new|create) -> ✅
  • SessionsController -> ✅ , but I think this works a bit differently to the other controllers anyway (see below).

I don't use any other Devise features in this app, so that was all I tested. Thank you so much for your work on this.


re SessionsController, for handling failed login attempts, I have this failure app (which is still necessary even with this PR):

class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      recall
      # turbo wants a 422 https://turbo.hotwire.dev/handbook/drive#redirecting-after-a-form-submission
      response[0] = 422
    else
      super
    end
  end
end

It's based on this: https://discuss.hotwire.dev/t/forms-without-redirect/1606/14

Would you be open to something like this being upstreamed to Devise?

@carlosantoniodasilva
Copy link
Member Author

carlosantoniodasilva commented Jan 18, 2021

@ghiculescu thanks for your thorough testing and feedback, really appreciated! What a coincidence, I just signed up to discuss.hotwire.dev and replied to that same thread, I had it opened here for a couple of days. 😄

I definitely want to get things working more out of the box with both responders & devise, yes, just playing catch up on many fronts here. I actually think the second method skip_format? can be refactored in Devise so it wouldn't require an override, but we might still need custom code in respond for turbo_stream. What I'm not sure is if turbo requires a specific 422 (as per that doc linked there), or if 401 would work (see the linked PR above, it mentions anything in the 4xx range), that's what I have to dig deeper. Anyway, the TL;DR is that yes, definitely open to getting Devise + Hotwire/Turbo working playing nice together. Thanks!

@ghiculescu
Copy link

I think any 4xx is fine. See hotwired/turbo#39

I am happy to help put together a Devise PR, if you're happy with the general gist of my approach. (If you'd prefer to do it yourself that's fine by me too!)

I actually think the second method skip_format? can be refactored in Devise so it wouldn't require an override

I actually don't think it is needed in my case anymore. I had it there from when I used to call redirect instead of recall.

@carlosantoniodasilva
Copy link
Member Author

@ghiculescu if you have the availability feel free to go ahead :), I'd be happy to help where I can. I'm wrapping up a few other things related to this branch, Devise + OmniAuth, etc., for the time being, before I can tackle anything Devise + Hotwire, so maybe I could only check that by next week. Thanks.

@ghiculescu
Copy link

Alright, I'll give it a look. For this PR is there anything else outstanding? It's looking great to me - we are running it in production.

@carlosantoniodasilva
Copy link
Member Author

@ghiculescu 👍

I was just giving it a little time to see if anyone else from the original issue would chime in. I'm glad to know it's working for you (and in production!). I'll give it until the weekend and plan for a new release then. Thanks for your help.

Copy link

@gobijan gobijan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the documentation also needs to get adjusted to the new behaviour.
On line 52 of lib/action_controller/responder.rb the Rails behaviour without responders gem would be equivalent to:
format.html { render :new, status: :unprocessable_entity } <= with the 422 behaviour now in it :)

@carlosantoniodasilva
Copy link
Member Author

Thanks @gobijan, those have been updated.

@gobijan
Copy link

gobijan commented Jan 25, 2021

No thank you so much for your continuous effort on so many great ruby gems :)!

@carlosantoniodasilva carlosantoniodasilva marked this pull request as ready for review January 29, 2021 11:21
@carlosantoniodasilva carlosantoniodasilva merged commit 413eae1 into master Jan 29, 2021
@carlosantoniodasilva carlosantoniodasilva deleted the ca-render-error-422 branch January 29, 2021 11:22
ghiculescu added a commit to ghiculescu/devise that referenced this pull request Feb 2, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
@ghiculescu
Copy link

@ghiculescu if you have the availability feel free to go ahead :)

A bit late, but here you go! heartcombo/devise#5340

ghiculescu added a commit to ghiculescu/devise that referenced this pull request Feb 3, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
ghiculescu added a commit to ghiculescu/devise that referenced this pull request Feb 3, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
ghiculescu added a commit to ghiculescu/devise that referenced this pull request Feb 3, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
ghiculescu added a commit to ghiculescu/devise that referenced this pull request Feb 3, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
@carlosantoniodasilva
Copy link
Member Author

@ghiculescu do you use responders on its own (i.e. respond_with) in other controllers? or just happen to use it through Devise?

I ask because I was having some trouble getting it to fully work on its own as it was triggering API behavior, I'll circle back on it but I think something might be missing here still to get it right. (like a proper to_turbo_stream handling in the responder)

@ghiculescu
Copy link

I only use it via devise. I can have a look if you can share some more details about your issue?

@carlosantoniodasilva
Copy link
Member Author

carlosantoniodasilva commented Feb 25, 2021

I'll take another look and maybe try with a fresh app to create a sample, but basically it was not working to return HTML properly when integrated with hotwire/turbo. I'll report back with more info, thanks.

thomasklemm pushed a commit to thomasklemm/devise that referenced this pull request Mar 31, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
yrashk pushed a commit to HackerIntro/devise that referenced this pull request Jul 24, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
@lutzcc1
Copy link

lutzcc1 commented Aug 17, 2021

Hi @carlosantoniodasilva, thanks for all this work!

I'm currently working on migrating an app to Turbo. I'm still unsure if I should handle Devise forms using a custom-made solution in production (not a fan of that idea) or if it would be better to wait for this change to be released with the new version of the Responders gem.

Are there plans to release this anytime soon?

miharekar pushed a commit to miharekar/devise that referenced this pull request Sep 4, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
liaden pushed a commit to liaden/devise that referenced this pull request Sep 28, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
miharekar pushed a commit to miharekar/devise that referenced this pull request Oct 6, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
ghiculescu added a commit to ghiculescu/devise that referenced this pull request Oct 15, 2021
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
@brendon
Copy link

brendon commented Oct 20, 2021

This is a very useful fix :) I was wondering if you'd consider cutting a new gem for this soon? :)

baarkerlounger added a commit to baarkerlounger/devise that referenced this pull request Jan 12, 2022
Devise `FailureApp` now returns a `422` status when running the `recall` 
flow. This, combined with heartcombo/responders#223, enables Devise to 
work correctly with the [new Rails defaults](rails/rails#41026) for form 
errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the 
[`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
yrashk pushed a commit to HackerIntro/devise that referenced this pull request Feb 16, 2022
Devise `FailureApp` now returns a `422` status when running the `recall` flow. This, combined with heartcombo/responders#223, enables Devise to work correctly with the [new Rails defaults](rails/rails#41026) for form errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the [`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
baarkerlounger added a commit to baarkerlounger/devise that referenced this pull request Feb 28, 2022
Devise `FailureApp` now returns a `422` status when running the `recall` 
flow. This, combined with heartcombo/responders#223, enables Devise to 
work correctly with the [new Rails defaults](rails/rails#41026) for form 
errors, as well as with new libraries like Turbo.

By default, the `recall` flow only runs on the 
[`SessionsController`](https://github.com/heartcombo/devise/blob/v4.7.3/app/controllers/devise/sessions_controller.rb#L48).
carlosantoniodasilva added a commit that referenced this pull request Jan 27, 2023
This essentially makes the previous change merged via #223 configurable,
so that we can keep the current behavior on existing apps, returning
`200 OK` even for errors, while changing the generator for new apps to
be configured with `422 Unprocessable Entity`, making them work better
with Hotwire/Turbo installations out of the box.

Please note that these defaults are likely to be swapped in a future
major version of Responders.
carlosantoniodasilva added a commit that referenced this pull request Jan 27, 2023
This essentially makes the previous change merged via #223 configurable,
so that we can keep the current behavior on existing apps, returning
`200 OK` even for errors, while changing the generator for new apps to
be configured with `422 Unprocessable Entity`, making them work better
with Hotwire/Turbo installations out of the box.

Please note that these defaults are likely to be swapped in a future
major version of Responders.
carlosantoniodasilva added a commit that referenced this pull request Jan 27, 2023
This essentially makes the previous change merged via #223 configurable,
so that we can keep the current behavior on existing apps, returning
`200 OK` even for errors, while changing the generator for new apps to
be configured with `422 Unprocessable Entity`, making them work better
with Hotwire/Turbo installations out of the box.

Please note that these defaults are likely to be swapped in a future
major version of Responders.
@carlosantoniodasilva
Copy link
Member Author

I forgot to ping back here, but v3.1.0 was released a couple weeks ago including this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Respond status.
5 participants