-
Notifications
You must be signed in to change notification settings - Fork 21.8k
Lock down new ImplicitRender
behavior for 5.0 RC
#23827
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
Conversation
ec49054
to
868127c
Compare
# | ||
# Third and last, if the current request is a real "interactive" browser request, | ||
# <tt>ActionView::MissingTemplate</tt> is raised to display a helpful error | ||
# message. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the docs felt a bit hard to read to me. Took a stab at rephrasing them.
Something feels funny about assumed exhaustive
too, but ¯_(ツ)_/¯.
Here's the originals:
# Handles the implicit behavior for a controller action when it did not
# explicitly call +render+ or otherwise indicate what the response should be,
# such as by calling +respond_to+, +redirect+ and +head+.
#
# If this controller action has any associated templates, it tries to render
# the most appropiate template for the current action, taking into account of
# the action name, format, locales, variants etc.
#
# The list of available templates is assumed to be exhausative – if a suitable
# template cannot be found (for example, when the client requested the XML
# format when the only templates in HTML and JSON formats are available), an
# <tt>ActionController::UnknownFormat</tt> error will be raised.
#
# On the other hand, when there are no templates associated with the controller
# action at all, it tries to determine if the current request is "interactive"
# (i.e. came from a real browser). If so, it raises the <tt>ActionView::MissingTemplate</tt>
# error in order to display a helpful error message. Otherwise, it assumes the
# request is an API request and renders a "204 No Content" response.
#
# For API controllers, the implicit render is always "204 No Content" without
# performing any template lookups.
Taking the baton from @kaspth, and doing some additions to this. r? @maclover7 |
Pushed up a commit with some changes. Feel free to squash down -- only pushed up a separate commit so that way others can review. Passing the baton back to @chancancode for the finale 😄 r? @chancancode |
4e25739
to
3196a99
Compare
3196a99
to
a30b7a9
Compare
ImplicitRender
behavior for 5.0 RCImplicitRender
behavior for 5.0 RC
"action but none of them were suitable for this request.\n\n" \ | ||
"This usually happens when the client requested an unsupported format " \ | ||
"(e.g. requesting HTML content from a JSON endpoint or vice versa), but " \ | ||
"it might also be failing to due to other constraints, such as locales " \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to due
458a268
to
cf8915b
Compare
1. Conceptually revert #20276 The feature was implemented for the `responders` gem. In the end, they did not need that feature, and have found a better fix (see heartcombo/responders#131). `ImplicitRender` is the place where Rails specifies our default policies for the case where the user did not explicitly tell us what to render, essentially describing a set of heuristics. If the gem (or the user) knows exactly what they want, they could just perform the correct `render` to avoid falling through to here, as `responders` did (the user called `respond_with`). Reverting the patch allows us to avoid exploding the complexity and defining “the fallback for a fallback” policies. 2. `respond_to` and templates are considered exhaustive enumerations If the user specified a list of formats/variants in a `respond_to` block, anything that is not explicitly included should result in an `UnknownFormat` error (which is then caught upstream to mean “406 Not Acceptable” by default). This is already how it works before this commit. Same goes for templates – if the user defined a set of templates (usually in the file system), that set is now considered exhaustive, which means that “missing” templates are considered `UnknownFormat` errors (406). 3. To keep API endpoints simple, the implicit render behavior for actions with no templates defined at all (regardless of formats, locales, variants, etc) are defaulted to “204 No Content”. This is a strictly narrower version of the feature landed in #19036 and #19377. 4. To avoid confusion when interacting in the browser, these actions will raise an `UnknownFormat` error for “interactive” requests instead. (The precise definition of “interactive” requests might change – the spirit here is to give helpful messages and avoid confusions.) Closes #20666, #23062, #23077, #23564 [Godfrey Chan, Jon Moss, Kasper Timm Hansen, Mike Clark, Matthew Draper]
cf8915b
to
73b1efc
Compare
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger | ||
super | ||
elsif any_templates?(action_name.to_s, _prefixes) | ||
message = "#{self.class.name}\##{action_name} does not know how to repond " \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
repond
Lock down new `ImplicitRender` behavior for 5.0 RC
Merging to unblock the release! We have some ideas for improvements (shortening the error messages and move the details into the HTML page like routing errors, bring back the "searched in..." info, add an config to turn off the "interactive request warning", fine-tune the browser-detection, perhaps express |
Thanks everyone for the hard work!!! |
❤️ |
🎉 |
On a separate note… I think this message is very explicit regarding the edge cases, but it does not convey exactly what to do in the case that will occur 99% of the times. The old message used to clearly state:
I think we can try to improve the current message and make it more accessible to newcomers, also given that this is part of the Rails guides:
|
@claudiob it's been rephrased because you aren't necessarily missing a file. If you expect a 204, then you don't need to do anything. |
@kaspth I understand and I agree with this PR, don't get me wrong. I just think that if you do miss a file (probably still the most common case), we are not telling you anymore where to put that file and which filenames are accepted. |
Yeah, agreed. And it's one @chancancode's suggestions in #23827 (comment). |
@claudiob please continue to improve it! The case to keep in mind is that..
So I think somehow conveying the fact that "this might be okay, actually" is important. But I like @matthewd's suggestion of putting less in the exception message (and probably bring back the info we had before) and put more in the specialized HTML error page. By the way, since the browser detection might not be perfect (an API explore extension in chrome or a BrokenKit.framework on iOS might send some browser-like headers), it is probably good to have a way to turn it off and mention that on the error page too. ("If you are expected the 204, everything is cool, don't worry. You can also turn this off via... if you like.") |
Thanks to @chancancode for all the work on this! FWIW, I strongly agree with @claudiob that the message would be improved by addressing the most common case of missing the file. The current message puts a lot of emphasis on the least common case. In fact, given the |
Noticing that this generally applies to We have some internal API controllers that take Think we can get the best of both worlds by pruning the "is a developer viewing this in the browser and expecting a response?" check down to non-XHR HTML GETs. Non-XHR PUT/POST/DELETE are typically redirects to an HTML GET. |
@jeremy I worry that'll mean "I forgot to do the redirect" becomes "my browser is pretending I'm not pressing the button" 😕 What If we really wanted to get creative, presence of cookies might be worth considering. |
Agree. I wager that forgetting a redirect is fairly uncommon and, hopefully, easier to diagnose. The form will submit and you'll get a white page of death in response. Checking for user-agent details gets tricky since more sophisticated API clients may appear browser-like in all those respects. Then a request could spookily seem to work with some clients but not others. |
…urpose?" heuristics Narrows the "are you in a browser, viewing the page?" check to exclude non-GET requests. Allows content-less APIs to use implicit responses without having to set a fake request format. This will need further attention. If you forget to redirect from a POST to a GET, you'll get a 204 No Content response that browsers will typically treat as… do nothing. It'll seem like the form just didn't work and knowing where to start debugging is non-obvious. On the flip side, redirecting from POST and others is the default, done everywhere, so it's less likely to be removed or otherwise missed. Alternatives are to do more explicit browser sniffing. Ref rails#23827.
In Rails 5 has changed the behavior of the render, to modify the expected value in accordance with new behavior. Ref: rails/rails#23827
In Rails 5 has changed the behavior of the render, to modify the expected value in accordance with new behavior. Ref: rails/rails#23827
In Rails 5 has changed the behavior of the render, to modify the expected value in accordance with new behavior. Ref: rails/rails#23827
In Rails 5 has changed the behavior of the render, to modify the expected value in accordance with new behavior. Ref: rails/rails#23827
Conceptually revert Allow default_render to take a block to customize behavior when there's no template #20276
The feature was implemented for the
responders
gem. In the end,they did not need that feature, and have found a better fix (see
Support rails5 heartcombo/responders#131).
ImplicitRender
is the place where Rails specifies our defaultpolicies for the case where the user did not explicitly tell us
what to render, essentially describing a set of heuristics. If
the gem (or the user) knows exactly what they want, they could
just perform the correct
render
to avoid falling through tohere, as
responders
did (the user calledrespond_with
).Reverting the patch allows us to avoid exploding the complexity
and defining “the fallback for a fallback” policies.
respond_to
and templates are considered exhaustive enumerationsIf the user specified a list of formats/variants in a
respond_to
block, anything that is not explicitly included should result
in an
UnknownFormat
error (which is then caught upstream tomean “406 Not Acceptable” by default). This is already how it
works before this commit.
Same goes for templates – if the user defined a set of templates
(usually in the file system), that set is now considered exhaustive,
which means that “missing” templates are considered
UnknownFormat
errors (406).
To keep API endpoints simple, the implicit render behavior for
actions with no templates defined at all (regardless of formats,
locales, variants, etc) are defaulted to “204 No Content”. This
is a strictly narrower version of the feature landed in Controller actions should default to head :ok if no template exists #19036 and
when a template is missing for the default render, do head no_content instead #19377.
To avoid confusion when interacting in the browser, these actions
will raise a
TemplateMissing
error for “interactive” requestsinstead. (The precise definition of “interactive” requests might
change – the spirit here is to give helpful messages and avoid
confusions.)
Closes #20666, #23062, #23077, #23564
[Godfrey Chan, Jon Moss, Mike Clark, Matthew Draper, Kasper Timm Hansen]
TODO: