Handle MEAN stack application address without '#' snippet
Edit 1: Here is the mini-code I did it to reproduce the bug. Follow README.md
to install.
Edit 2: Finally I found one solution. Also, $locationProvider.html5Mode(true);
( $locationProvider.hashPrefix('')
NOT NECESSARY for me) and <base href="/" />
, I need to add the following in routes/index.js
, not app.js
. Then we don't need to add anything else to app.js
either nginx or apache like this thread .
var express = require('express');
var router = express.Router();
var path = require('path');
... ...
router.get('*', function(req, res) {
res.sendfile('./views/index.html'); // load our public/index.html sendFile
// res.sendFile('index.html', { root: path.join(__dirname, 'views') }); // does not work
});
One problem: in the server console, it gives express deprecated res.sendfile: Use res.sendFile instead routes/index.js:461:9
. But res.sendFile('index.html', { root: path.join(__dirname, 'views') });
can't help it, it returns 404 error.
My express version ~4.14.0
... Does anyone know how to fix this?
OP:
I am developing on Mac with Apache a MEAN stack application that can be queried
https://localhost:3000/#/home
. In production with an NGINX server, the application can be requested
https://www.myapp.io/#/home
. The fragment identifier is #
required in all cases because of angular ui-router
.
So, I wanted to make a nice url without #
(eg https://www.myapp.io/home
, https://localhost:3000/home
). I did the following:
-
added
$locationProvider.html5Mode(true); $locationProvider.hashPrefix('')
inapp.config(['$stateProvider'...
. -
added
<base href="/" />
toindex.html
As a result, it https://localhost:3000/#/home
automatically changes to https://localhost:3000/home
in the browser bar, similar to https://www.myapp.io/#/home
.
However, direct entry https://localhost:3000/home
or https://www.myapp.io/home
in the browser will throw an error (I don't know how to turn the previous one <h1><%= message %></h1><h2><%= error.status %></h2><pre><%= error.stack %></pre>
into error.ejs
before error.html
), so I don't have more details).
So, now you need to do https://localhost:3000/home
and https://www.myapp.io/home
.
Following this thread , I added the following to app.js
:
app.use('/js', express.static(__dirname + '/js'));
app.use('/dist', express.static(__dirname + '/../dist'));
app.use('/css', express.static(__dirname + '/css'));
app.use('/partials', express.static(__dirname + '/partials'));
app.all('/*', function(req, res, next) {
res.sendFile('index.html', { root: __dirname });
});
And on Apache Mac, here's mine httpd-vhosts.conf
, after restarting apache
,
https://localhost:3000/home
still returning an error.
<VirtualHost *:443>
ServerName localhost
DocumentRoot "/Users/SoftTimur"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/apache2/ssl/localhost.crt
SSLCertificateKeyFile /etc/apache2/ssl/localhost.key
<Directory "/Users/SoftTimur">
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
</VirtualHost>
In production, this is an NGINX server block. NGINX https://www.myapp.io/home
still returns error after restarting .
server {
listen 443 ssl;
server_name myapp.io www.myapp.io;
ssl_certificate /etc/letsencrypt/live/myapp.io/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.io/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:EC$
ssl_session_timeout 1d;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
index index.html;
root /opt/myapp;
location / {
try_files $uri $uri/ /index.html;
}
location ~ /.well-known {
allow all;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Accept-Encoding "";
proxy_set_header Proxy "";
proxy_pass https://127.0.0.1:3000;
# These three lines added as per https://github.com/socketio/socket.io/issues/1942 to remove sock$
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Can anyone please help?
try this on your express server
var express = require('express');
var app = express();
app.get('*', function (req, res) { res.sendFile(__dirname + '/views/index.html'); });
and in your angular app:
$locationProvider.hashPrefix('!').html5Mode({
enabled: true
});
$urlRouterProvider.otherwise('/');
and you still need <base href="/">
in your index.html
let me know if it works for you
EDIT:
I just found your app at https://github.com/chengtie/mini-mean , it looks like your app.use order is wrong. Copy this to your express server and check if it's good now. pastebin
This can be helpful,
Angular routing without hash '#'
Also, use this line in express server file.
app.use(express.static(path.join(__dirname, 'client folder')));
this will directly find your index.html file in that views folder and load it
- You don't need Apache or Nginx to run NodeJs in development, just
node server.js
enough - Express gave you this error because you are using a deprecated API
res.sendfile
, please use res.sendFile (capital F) - Additional information for SPA:
- When you have a "#" in your url, the browser interprets it as a local link and therefore won't send a new request to the server
- By enabling
$locationProvider.html5Mode(true)
, you are now using html5 push state to navigate through app history and (if I'm not mistaken what you are using) angular effectively remove the '#' in url - Without browser '#' (hash-bang) it will interpret it as a new request and send it to the server, so you need to map all requests from the server to your SPA login file
- For the exact steps to replicate this behavior refer to this article: https://scotch.io/tutorials/pretty-urls-in-angularjs-removing-the-hashtag (the base href value in the input file is important)
If it's around #. You can remove it in Angular yourself.
Just inject locationProvider into your app entry and set htmlMode to true. In your index.html, set the baseUrl.
$locationProvider.html5Mode(true)
And you have index.html add:
<base href="/" />
This will generate your urls without #. Does it help?