-
Notifications
You must be signed in to change notification settings - Fork 415
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
Restrict Accept: turbo-stream to Form Submissions #52
Conversation
f9bab22
to
2c9ebec
Compare
There are plenty of reasons to send a turbo stream response to a GET. Replacing a Observe also the request for programmatic access in #34. I suspect there will be more uses for turbo stream responses in the future, not fewer. |
This change set proposes that navigational GET requests should omit the Turbo Steam content type from the headers. The
I'm not familiar with the tbody example. Could you share a link? To me, these use cases seem to by good matches for GET-powered turbo frames.
If there were a |
A dry read through the diff seemed to indicate that turbo frame GETs would also be affected? since they only deliver
#48 here, but also repeated by others in private spaces, and it's a well-known issue besides when working with component-style custom elements.
In general, the opposite is so; autonomous custom elements are phrasing content, so they're not permitted in tables, or other contexts where they'd have obvious potential otherwise e.g. This would also be instructing other folks how to go about their business, which some may consider anathematic. Another possibility could be merging the turbo-frame element, or its constituent parts, into the prototypes of custom extensions to existing table elements, which the DOM should hopefully then permit.
I was rather expecting to simply fire a If the intention is to identify Turbo Drive GETs contextually to shave a line or two of format negotiation from controller methods, then I'd definitely suggest circling around on the signalling mechanisms and consider how to elegantly achieve just that, rather than carving it as negative space from other parts. |
7dd1282
to
b7a1187
Compare
b7a1187
to
2957bfb
Compare
2957bfb
to
4cb9fa1
Compare
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.
Looks good! Already reviewed the enctype changes, so this is pretty simple really.
4cb9fa1
to
7b883d5
Compare
7b883d5
to
01406de
Compare
Since Turbo Stream responses over HTTP are in response to user interactions, and the conventional way to trigger destructive user interactions is through form submissions, this commit limits the `StreamObserver` _only_ inject the [Accept][] header containing the `text/vnd.turbo-stream.html` content type during form submissions. Replace the [`turbo:before-fetch-request` event listener][turbo-event] with a `turbo:submit-start` event listener. From a Rails perspective, this enables an interesting pattern. Consider the following controller: ```ruby class MessagesController < ApplicationController def create @message = Message.new(params.require(:message).permit(:body)) if @message.save redirect_to messages_url else render :new end end end ``` By omitting a [respond_to][] block in the controller's action, the controller offloads the rendering responsibility to the view layer, enabling the presence or absence of a template to be the deciding factor on the type of response: ```html+erb <%# app/views/messages/new.html.erb %> <%= turbo_frame_tag @message do %> <%= render partial: "form", locals: { message: @message } %> <% end %> <%# app/views/messages/new.turbo_stream.erb %> <%= turbo_stream.replace @message do %> <%= render partial: "form", locals: { message: @message }, formats: :html %> <% end %> ``` Prior to this change, `GET` requests initiated by Turbo Drive navigations were submitting requests with the content type, resulting in requests to the `messages#new` being handled by the `messages/new.turbo_stream.erb` template, instead of the fully-formed HTML responses provided by the `messages/new.html.erb` template. [Accept]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept [turbo-event]: https://turbo.hotwire.dev/reference/events [respond_to]: https://edgeapi.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_to
01406de
to
09c0f4a
Compare
Closes #141. Follows-up #52. --- When creating the headers for a `FetchRequest`, provide the default headers to the delegate by adding an argument to the `additionalHeadersForRequest` signature so that delegates can incorporate the current values if they choose to. They're passed as a copy to prevent delegates from destructively acting upon them, with the intention that delegates merge values _into_ them. Testing --- Add a guard middleware to the test server to reject _all_ requests that don't specify an [Accept][] header containing `"text/html, application/xhtml+xml"`. Next, guard Stream requests with a similar check for `"text/vnd.turbo-stream.html"`. Since those endpoints are behind the guard middleware, they'll also require `"text/html, application/xhtml+xml"` as well. Finally, replace the Stream functional test's explicit call to `fetch` with a `<form>` element submitting with the same content. [Accept]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
Documents hotwired/turbo#52 --- Explain that Turbo Stream requests are only made within `<form>` element submissions that are not `GET` requests. Additionally, provide an example of how to turn other types of requests into Turbo Stream requests.
Documents hotwired/turbo#52 --- Explain that Turbo Stream requests are only made within `<form>` element submissions that are not `GET` requests. Additionally, provide an example of how to turn other types of requests into Turbo Stream requests.
Documents hotwired/turbo#52 --- Explain that Turbo Stream requests are only made within `<form>` element submissions that are not `GET` requests. Additionally, provide an example of how to turn other types of requests into Turbo Stream requests.
* Document Turbo Stream Accept: header changes Documents hotwired/turbo#52 --- Explain that Turbo Stream requests are only made within `<form>` element submissions that are not `GET` requests.
Sorry to bump this, but if the goal is to remove the turbo-stream header from navigational GETs, shouldn't the forms with Example use case: <form method="get" action="">
<input type="search" name="s">
<button type="submit">Search</button>
</form>
<ul id="results">[Whatever]</ul> Server side (pseudocode) :
The form could be a POST, but then you wouldn't have the |
@seanpdoyle @dhh Given the number of issues and questions regarding Turbo stop handling GET requests (see kaminari/kaminari#1067 (comment)), I think it would be great if this could be reconsidered. I too find it immensely useful to have a link trigger a GET request for a What about if regular links included the turbo Accept header, but only if they were annotated with |
I'd be happy to see a way where we can force a GET to process turbo streams. I think the data-turbo-method=get is an interesting approach. Could also go with a specific one like data-turbo-stream=true. Thoughts @seanpdoyle? |
Happy that you agree that there should be a way of achieving this! My vote would be for |
Further to using a specific one is there currently, or could there be, support for adding this to a link_to helper? Maybe with an easy option like "turbo_stream: true"? In this case setting data: {turbo_method: :get} on a request that is :get by default might make it less confusing. Or would this be outside the scope for a link_to helper? |
@cpanderson Yes, I think the @seanpdoyle Sean, first of all, thank you for the huge contribution on Turbo. I'm learning a lot from https://github.com/thoughtbot/hotwire-example-template (for the ones that don't know, there's a different branch for every tutorial there). Sorry to bump this, but did you have any time to consider this proposal? By the number of votes on DHH's comment above, it seems this would be a greatly appreciated addition! |
Is writing to the request headers something that applications could opt-into themselves using events that are already available? addEventListener("submit", ({ target: formElement, submitter }) => {
const forcesTurboStream = submitter?.hasAttribute("data-turbo-stream") || target.hasAttribute("data-turbo-stream")
if (forcesTurboStream) {
formElement.addEventListener("turbo:submit-start", ({ detail: { formSubmission }) => {
formSubmission.fetchRequest.headers["Accept"] = "text/vnd.turbo-stream.html"
}, { once: true })
}
}) |
Turbo Streams are normally supported only for [non-GET requests][0]. However there are cases where Turbo Streams responses to GET requests are useful. This commit adds the ability to use Turbo Streams with specific GET requests by setting `data-turbo-stream="true"` on a form or link. [0]: #52
Does this mean the above needs to be written for every form that needs it? I'm doing a |
Closes #139
Since Turbo Stream responses over HTTP are in response to user
interactions, and the conventional way to trigger destructive user
interactions is through form submissions, this commit limits the
StreamObserver
only inject the Accept header containing thetext/html; turbo-stream
content type during form submissions.Replace the
turbo:before-fetch-request
event listenerwith a
turbo:submit-start
event listener.From a Rails perspective, this enables an interesting pattern. Consider
the following controller:
By omitting a respond_to block in the controller's action, the
controller offloads the rendering responsibility to the view layer,
enabling the presence or absence of a template to be the deciding factor
on the type of response:
Prior to this change,
GET
requests initiated by Turbo Drivenavigations were submitting requests with the content type, resulting in
requests to the
messages#new
being handled by themessages/new.turbo_stream.erb
template, instead of the fully-formedHTML responses provided by the
messages/new.html.erb
template.