Skip to content
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

Adds modrewrite functionality (SPA HTML5 location history) #86

Closed
ajsb85 opened this issue Dec 8, 2016 · 17 comments
Closed

Adds modrewrite functionality (SPA HTML5 location history) #86

ajsb85 opened this issue Dec 8, 2016 · 17 comments

Comments

@ajsb85
Copy link

ajsb85 commented Dec 8, 2016

Hi @kzahel

I want this feature.
To support $locationProvider.html5Mode(true);

var modRewrite = require('connect-modrewrite');
modRewrite(['^[^\\.]*$ /index.html [L]'])
@ajsb85 ajsb85 changed the title Mod Rewrite adds modrewrite functionality to the Web Server for Chrome Adds modrewrite functionality to the Web Server for Chrome Dec 8, 2016
@ajsb85 ajsb85 changed the title Adds modrewrite functionality to the Web Server for Chrome Adds modrewrite functionality Dec 8, 2016
@kzahel
Copy link
Owner

kzahel commented Dec 8, 2016

I'm not familiar with modrewrite. What would that do? Also what is that regexp, anything ending in a slash, at the end of a string? What's the [L]

WSC does not use configuration files, the interface to configure options is just checkboxes...

@ajsb85
Copy link
Author

ajsb85 commented Dec 9, 2016

Add a new checkbox for html5Mode on.

And if the entry does not exist redirect to the index.html.

The HTML5 History API

Modern browsers give developers the ability to programmatically create new browser history entries that alter the displayed URL without the need for a new request. Using the history.pushState method developers have full control of the browser's navigational history for an application.

@cgriffin4
Copy link

This functionality is required for running many of the Polymer SPAs, I think those projects would benefit greatly by Chrome Web Server but it would require this option.

@kzahel
Copy link
Owner

kzahel commented Feb 16, 2017

Fascinating. So in this mode, every URL simply goes to the /index.html at the root?

Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links to entry point of your application (e.g. index.html). Requiring a tag is also important for this case, as it allows AngularJS to differentiate between the part of the url that is the application base and the path that should be handled by the application.

from: https://docs.angularjs.org/guide/$location#html5-mode

Note that angular docs themselves seem not to use modrewrite :-)

@cgriffin4
Copy link

cgriffin4 commented Feb 16, 2017

@kzahel I'm not that familiar with angular but the two major ways of handling SPA routing are the hashbang or a server rewrite.

The hashbang method requires URLs to be transformed and ugly, from your link you can see the example http://example.com/#!/foo/bar?x=y which of course will request http://example.com/index.html which then will run code to handle the routing.

Alternately, you configure the server to pass all extensionless requests to /index.html. This way the URL is http://example.com/foo/bar?x=y and the server sees the request and gives it to the index page at the root of the app which still handles the routing. Of course, you can't redirect all request as you'll still want to load all your assets but since they'll be associated with an extension you just ignore those in your rewrite.

@ajsb85 showed how to do it on a node server

var modRewrite = require('connect-modrewrite');
modRewrite(['^[^\\.]*$ /index.html [L]'])

A web.config file example would be:

<system.webServer>
    <rewrite>
      <rules>
        <rule name="Extensionless SPA">
          <match url=".*\.[\d\w]+$" negate="true"/>
          <action type="Rewrite" url="/index.html"  />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

I'm not sure how easy it would be to implement in this project (which I love! and by the way THANKS!). But it'd be a useful feature for SPA development.

Edit: I should also mention I'm like 97% sure this is what @thevukaslt is trying to do in issue #89

@kzahel
Copy link
Owner

kzahel commented Feb 16, 2017

@cgriffin4 thanks for the detailed info.
So here's what I'm thinking. I add an advanced option for HTML5 history modrewrite, with a checkbox and a little text input for specifying the regexp that should redirect to /index.html

This means any request for any asset matching the regexp will not actually look for a file on the disk with that path, but instead index.html

With your example ".*.[\d\w]+$" regexp, what does this try to match? I'm guessing there are a lot of regexps people would use, but wouldn't most people exclude something like a "/static/" "/public/" or "/assets" folder?

@cgriffin4
Copy link

cgriffin4 commented Feb 16, 2017

@kzahel You could handle it by having directories to exclude like you mention with "/static", etc but I usually just assume if it doesn't have an extension then it's requesting a location on the site (so serve the site, which is just /index.html). If it has an extension then it's a file being requested so serve that file. The regex in my example simply picks up on any request that does not have a file extension.

Currently (with auto index.html turned on):
http://localhost:8887/ serves -> http://localhost:8887/index.html
http://localhost:8887/directory/ serves -> http://localhost:8887/directory/index.html
http://localhost:8887/directory/file.ext serves -> http://localhost:8887/directory/file.ext

The desired option would be that when serving index.html use the root directory. So the only change from above is the second example. That one becomes:
http://localhost:8887/directory/ serves -> http://localhost:8887/index.html

@kzahel kzahel changed the title Adds modrewrite functionality Adds modrewrite functionality (SPA HTML5 location history) Feb 16, 2017
@kzahel
Copy link
Owner

kzahel commented Feb 16, 2017

@cgriffin4 with your example (any url with a dot is not rewritten)
would query parameters get rewritten/destroyed too? or should i keep those on
e.g.
rewrite /products/foo/?blah=1 to /index.html or to /index.html?blah=1

@bbarrows
Copy link

bbarrows commented Feb 16, 2017

Sorry, worded that poorly.

I have not used connect-modrewrite myself and was wondering if

var app = connect()
.use(modRewrite(['^[^\\.]*$ /index.html [L]']))
.use(connect.static(options.base))
.listen(3000)

Would first try to find the static files before using the modRewrite (which redirects all back to index.html I believe?)?

Ill setup a test I guess to figure that out

@cgriffin4
Copy link

@kzahel The idea is to keep the request completely.

Basically, the easiest way to think of it is an alternative to "Automatically show index.html". This is simply "Automatically show /index.html". So the url isn't redirected, altered, or destroyed.

Right now we have the choice of displaying a directory contents, or displaying the index.html file in a directory. While this is great, it's arguably more common that you'd want to display a single index.html file instead of having a site with multiple index.html files. So this would mean that when browsing to a directory (or non-existent directory) there would be an option that would load a global index.html (commonly located in the root, thus /index.html).

In fact, if the code was easier and you didn't want to add another option then if (this.app.opts.optRenderIndex) and the folder exists then load /valid/location/index.html but if the folder doesn't exist then load /index.html

@bbarrows I'm not sure about the regex but what I usually do is allow for /valid/location/index.html but if /virtual/path is requested then serve /index.html

@cgriffin4
Copy link

cgriffin4 commented Feb 16, 2017

@kzahel I wouldn't suggest using this hack directly as it's hacky but it's an example of a simple solution which shouldn't break many other use cases.

If you put the following line in the file handler.js at line number 235 it produces the desired effect for SPA.

if (entry.error && (this.app.opts.optRenderIndex && !this.request.path.match(/[^/]*\.[^.]*$/i))) { entry = this.fs.entry; }

So if the entry was not found in the filesystem and the option to show index.html is true and the path requested does NOT have an extension (could possibly simplify that regex) then default to the root directory (which will then default to showing the index.html in the root directory).

Tested it on the Polymer News PWA and it loads fine with that change.

It does mean someone could expect to get a 404 if they wanted to load /directory/index.html but mistyped the link as /dicrectory/. In this case it would route unexpectedly to the root but first, I think that is reasonable and secondly, like I said it is a hacky solution.

@kzahel
Copy link
Owner

kzahel commented Feb 17, 2017

I added an option to a new branch. I haven't tested it much, maybe you could help
b041c48

The branch is modrewrite. You'll probably have to go to polymer-ui folder and run bower update and run build.sh to update the frontend (now using tooltips)

screen shot 2017-02-16 at 5 43 02 pm

@kzahel
Copy link
Owner

kzahel commented Nov 8, 2017

This is now in the current live version. I haven't received any feedback though if it's working.

@ajsb85
Copy link
Author

ajsb85 commented Nov 8, 2017

I will test it

@rafaelclp
Copy link

Working fine, the only problem is that the response is not including the content-type, thus the browser doesn't know it's an html page.

127.0.0.1:8887/hello serves /index.html, but as a text file.

@kzahel
Copy link
Owner

kzahel commented Nov 18, 2017 via email

@ethanaobrien
Copy link

@kzahel should this be closed?

@kzahel kzahel closed this as completed May 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants