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?
source to share
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
source to share
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
source to share
- 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)
source to share
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?
source to share