{"action":"created","issue":{"url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/54","repository_url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server","labels_url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/54/labels{/name}","comments_url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/54/comments","events_url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/54/events","html_url":"https://github.com/ember-fastboot/ember-fastboot-server/issues/54","id":152906240,"number":54,"title":"HTTP Redirection in Ember FastBoot","user":{"login":"arjansingh","id":1598641,"avatar_url":"https://avatars.githubusercontent.com/u/1598641?v=3","gravatar_id":"","url":"https://api.github.com/users/arjansingh","html_url":"https://github.com/arjansingh","followers_url":"https://api.github.com/users/arjansingh/followers","following_url":"https://api.github.com/users/arjansingh/following{/other_user}","gists_url":"https://api.github.com/users/arjansingh/gists{/gist_id}","starred_url":"https://api.github.com/users/arjansingh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arjansingh/subscriptions","organizations_url":"https://api.github.com/users/arjansingh/orgs","repos_url":"https://api.github.com/users/arjansingh/repos","events_url":"https://api.github.com/users/arjansingh/events{/privacy}","received_events_url":"https://api.github.com/users/arjansingh/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2016-05-04T00:39:24Z","updated_at":"2016-05-05T11:25:13Z","closed_at":null,"body":"# HTTP Redirection in Ember FastBoot\r\n\r\n## Contributors\r\n\r\n@pwfisher, @marcoow, and @briangonzalez. Many thanks for your help!\r\n\r\n## Summary\r\n\r\nRedirects 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.\r\n\r\nAccordingly, FastBoot should disallow `transitionTo` because it breaks the \"Back\" button. Instead, FastBoot should support `replaceWith` for redirection.\r\n\r\nFastBoot 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.\r\n\r\nThis could be implemented by 1) reopening `Ember.Router` and modifying [`replaceWith`](https://github.com/emberjs/ember.js/blob/v2.5.0/packages/ember-routing/lib/system/router.js#L354) 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.\r\n\r\n## When do Ember Applications redirect?\r\n\r\nEmber navigation methods typically amend navigation history; however, those same methods are occasionally used to redirect the user.\r\n\r\n### Examples:\r\n\r\n- **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`.\r\n- **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`.\r\n- **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`.\r\n\r\n## How should FastBoot Server redirect users?\r\n\r\n### Transitions can break the \"Back\" Button\r\n\r\nRedirection [should not break](https://www.w3.org/QA/Tips/reback) browser history. In Ember, navigation methods alter browser history (via HTML5) as follows:\r\n\r\nMethod                     | HTML5 History\r\n-------------------------- | -------------\r\n`intermediateTransitionTo` | none\r\n`transitionTo`             | PUSH\r\n`transitionToRoute`        | PUSH\r\n`Transition#retry`         | PUSH\r\n`replaceWith`              | REPLACE\r\n\r\nBecause `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.\r\n\r\n### FastBoot should redirect with `replaceWith`\r\n\r\n`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.\r\n\r\n### FastBoot should never call `transitionTo`\r\n\r\n`transitionTo` should only be called client-side.\r\n\r\n### FastBoot should only redirect `GET` requests\r\n\r\nBecause 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.\r\n\r\n### FastBoot should support both temporary and permanent redirection\r\n\r\nWhile 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.\r\n\r\nFastBoot should default to temporary redirection since permanent can be harmful.\r\n\r\n## How should Ember Applications and FastBoot Server divide responsibilities?\r\n\r\nRedirects in the Ember Application should:\r\n\r\n1. Abort the rendering of the page,\r\n2. Generate an appropriate location header,\r\n3. Generate an appropriate HTTP status code header, and\r\n4. Pass the headers back to the server middleware layer.\r\n\r\n**Open Question: How do we abort rendering?**\r\n\r\nRedirects that FastBoot Server receives should be sent back to the browser, so that it can update itself accordingly and request the new URL.\r\n\r\nBecause 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.\r\n\r\n## Potential Implementations\r\n\r\n### Reopen `Ember.Router` and modify `replaceWith`\r\n\r\nOne 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.\r\n\r\n### Hook into `Router#willTransition`\r\n\r\nAnother 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.\r\n\r\n### Implement `FastBoot#redirect`\r\n\r\nA 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.\r\n\r\nThis 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`.\r\n\r\n## Appendix\r\n\r\n### HTTP Redirection Codes\r\n\r\nAll responses must provide a target URI to the client.\r\n\r\n_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.\r\n\r\nClients can redirect without user permission for `GET` and `HEAD` request methods. Other request methods require user permission.\r\n\r\nCode  | Status Text        | Redirect Type | Redirecting Request Method\r\n----- | ------------------ | ------------- | --------------------------\r\n`301` | Moved Permanently  | Permanent     | May be changed\r\n`302` | Found              | Temporary     | Cannot change*\r\n`303` | See Other          | Temporary     | Must be `GET`\r\n`307` | Temporary Redirect | Temporary     | Cannot change\r\n`308` | Permanent Redirect | Permanent     | Cannot change\r\n\r\n\\* 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`.\r\n\r\n### Further Reading\r\n\r\n- [Ember.Route](http://emberjs.com/api/classes/Ember.Route.html)\r\n- [W3C HTTP/1.1 Status Codes](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)\r\n- [Demystifying redirection in Ember](http://www.briangonzalez.org/demystifying-redirection-ember/)\r\n- [User standard redirects: don't break the back button!](https://www.w3.org/QA/Tips/reback)\r\n"},"comment":{"url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/comments/217128622","html_url":"https://github.com/ember-fastboot/ember-fastboot-server/issues/54#issuecomment-217128622","issue_url":"https://api.github.com/repos/ember-fastboot/ember-fastboot-server/issues/54","id":217128622,"user":{"login":"marcoow","id":1510,"avatar_url":"https://avatars.githubusercontent.com/u/1510?v=3","gravatar_id":"","url":"https://api.github.com/users/marcoow","html_url":"https://github.com/marcoow","followers_url":"https://api.github.com/users/marcoow/followers","following_url":"https://api.github.com/users/marcoow/following{/other_user}","gists_url":"https://api.github.com/users/marcoow/gists{/gist_id}","starred_url":"https://api.github.com/users/marcoow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcoow/subscriptions","organizations_url":"https://api.github.com/users/marcoow/orgs","repos_url":"https://api.github.com/users/marcoow/repos","events_url":"https://api.github.com/users/marcoow/events{/privacy}","received_events_url":"https://api.github.com/users/marcoow/received_events","type":"User","site_admin":false},"created_at":"2016-05-05T11:25:13Z","updated_at":"2016-05-05T11:25:13Z","body":">  Using Ember.HistoryLocation\r\n\r\n> This looks like a potentially clean solution. The only issue is that we still need to be able to configure the status code for the redirect and I'm not sure how you could that cleanly without touching replaceWith or transitionTo or hooking into Ember.Router. @rwjblue, any thoughts on how to accomplish that? If we can answer that I'll add it as a potential solution to the RFC.\r\n\r\nI guess the idea would be to introduce sth. like `FastBootLocation` that would just abort the render and send a redirect on any route change? Regarding status code I think the only option is to always send 302 (temporary redirect) as there's no way of knowing whether a transition/redirect is intended to be temporary or permanent so that falling back to a permanent redirect is the only option we have really (in the vast majority of cases 302 will be correct anyway I think as there aren't a lot of cases where the Ember app would want future requests to a route to go to the redirect target probably)."}}