Skip to content

HTTP Redirection in Ember FastBoot #54

@arjansingh

Description

@arjansingh

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 to http://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 with id 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 of http://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:

  1. Abort the rendering of the page,
  2. Generate an appropriate location header,
  3. Generate an appropriate HTTP status code header, and
  4. 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.

Further Reading

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions