-
Notifications
You must be signed in to change notification settings - Fork 68
Description
HTTP Redirection in Ember FastBoot
Contributors
@pwfisher, @marcoow, and @briangonzalez. Many thanks for your help!
Summary
Redirects should not break the "Back" button. Redirecting with transitionTo
breaks the "Back" button by adding to browser history. replaceWith
replaces the current location in the browser's history so that the browser "Back" button will behave as expected.
Accordingly, FastBoot should disallow transitionTo
because it breaks the "Back" button. Instead, FastBoot should support replaceWith
for redirection.
FastBoot can extend replaceWith
to pass HTTP redirect status codes back to FastBoot Server, which would add it to the response along with the updated URL.
This could be implemented by 1) reopening Ember.Router
and modifying replaceWith
directly; 2) utilizing the willTransition
hook for Routes; or 3) adding a replaceWith
method to the FastBoot service and requiring developers to explicitly use that to opt in to FastBoot.
When do Ember Applications redirect?
Ember navigation methods typically amend navigation history; however, those same methods are occasionally used to redirect the user.
Examples:
- User State Changes: Sometimes a user's state within the application requires them to be redirected. For example, a user tries to access
http://app/protected/page
, but is unauthenticated. Here, we redirect them tohttp://app/login
. - Impermanent Routes: If a route's existence is temporary, we might redirect the user to a permanent location for better user experience. For example, a user requests
http://app/projects/14
, but the project withid
14 was deleted. Here, we redirect them to the main projects page,http://app/projects
. - Permanently Redirected Routes: If our application contains deprecated routes, vanity routes, or aliased routes, we would transition requests to the new routes while preserving navigation conventions for the user. For example if a user requested
http://app/projects/14
, but the project is now grouped under Organization one, we would redirect them to the new route ofhttp://app/organization/1/projects/14
.
How should FastBoot Server redirect users?
Transitions can break the "Back" Button
Redirection should not break browser history. In Ember, navigation methods alter browser history (via HTML5) as follows:
Method | HTML5 History |
---|---|
intermediateTransitionTo |
none |
transitionTo |
PUSH |
transitionToRoute |
PUSH |
Transition#retry |
PUSH |
replaceWith |
REPLACE |
Because transitionTo
adds a redirected URL to the browser history, it breaks the "Back" button by not allowing the user to navigate back past the redirect.
FastBoot should redirect with replaceWith
replaceWith
should be used for redirection in FastBoot because it does not add a redirected URL to the browser history. This both matches the specified behavior of HTTP 30X
status codes and does not break the "Back" button.
FastBoot should never call transitionTo
transitionTo
should only be called client-side.
FastBoot should only redirect GET
requests
Because FastBoot Server is only concerned with rendering web pages, it should only handle GET
requests. Any other HTTP methods should be handled by other middleware or by a different part of the application's front-end stack.
FastBoot should support both temporary and permanent redirection
While there is a compelling case for supporting only only temporary redirection codes, there are many situations where an Ember Application would need to support permanent redirection. In the case of Impermanent Routes, the application would want to signal the browser to update any bookmarked routes. For Permanently Redirected Routes, the same logic applies.
FastBoot should default to temporary redirection since permanent can be harmful.
How should Ember Applications and FastBoot Server divide responsibilities?
Redirects in the Ember Application should:
- Abort the rendering of the page,
- Generate an appropriate location header,
- Generate an appropriate HTTP status code header, and
- Pass the headers back to the server middleware layer.
Open Question: How do we abort rendering?
Redirects that FastBoot Server receives should be sent back to the browser, so that it can update itself accordingly and request the new URL.
Because FastBoot Server is middleware, developers can always intercept the request and handle the redirection before it reaches the client, though this is less desirable because it hides the redirection from the browser.
Potential Implementations
Reopen Ember.Router
and modify replaceWith
One way to implement this functionality is to reopen Ember.Router
and modify Router#replaceWith
to take an optional status code, abort rendering, and have it determine the new location. This solution is the most transparent to the Ember Application, but opens up a hard dependency on Ember.Router
internals.
Hook into Router#willTransition
Another way to implement this functionality is to hook into willTransition
, as the point where we abort rendering, generate a status code, and determine a new location. This is not as tightly coupled to Ember.Router
, but does present a less optimal interface for passing along the status code and determining the new location.
Implement FastBoot#redirect
A third way is to create a redirect method on the FastBoot service that would abstract client-side vs server-side complexity from developers. FastBoot#redirect
would take the transition arguments plus an optional redirect status code.
This approach would not tightly couple FastBoot code with Ember.Router
. But it would result in FastBooted Applications having to modify their application to use FastBoot#redirect
everywhere they might use a Route#replaceWith
.
Appendix
HTTP Redirection Codes
All responses must provide a target URI to the client.
Permanent redirection requires the client to update all cached references with the provided URI. Temporary redirection requires the client to not remember the provided URI and send all subsequent requests to the original URI.
Clients can redirect without user permission for GET
and HEAD
request methods. Other request methods require user permission.
Code | Status Text | Redirect Type | Redirecting Request Method |
---|---|---|---|
301 |
Moved Permanently | Permanent | May be changed |
302 |
Found | Temporary | Cannot change* |
303 |
See Other | Temporary | Must be GET |
307 |
Temporary Redirect | Temporary | Cannot change |
308 |
Permanent Redirect | Permanent | Cannot change |
* 302 status codes are not reliably implemented across browsers. Some browsers implement the request method specification properly. Others require the provided resource to be requested via GET
similar to 303
.