Tuesday, May 23, 2017

Handle the MEAN-stack application address without fragment '#'

Leave a Comment

Edit 1: Here is a mini code I made that reproduces the error. Please follow README.md to install.

Edit 2: Finally, I found one solution. Besides $locationProvider.html5Mode(true); ($locationProvider.hashPrefix('') is NOT necessary for me) and <base href="/" />, I need to add the follows in routes/index.js, rather than app.js. Then, we do NOT need to add anything more to app.js or nginx or apache like this thread mentions.

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 is, 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 not help, it returns 404 error.

My express version is ~4.14.0... does anyone know how to fix that?

OP:

I develop in Mac with Apache a MEAN-stack application that can be requested by https://localhost:3000/#/home. In production with an NGINX server, the application can be requested by https://www.myapp.io/#/home. The fragment-identifier # is needed in all cases because of angular ui-router.

So I wanted to make pretty url without # (eg, https://www.myapp.io/home, https://localhost:3000/home) work. I have done the following:

  1. added $locationProvider.html5Mode(true); $locationProvider.hashPrefix('') in app.config(['$stateProvider'....

  2. added <base href="/" /> in index.html

As a result, https://localhost:3000/#/home changes automatically to https://localhost:3000/home in the browser bar, similarly for https://www.myapp.io/#/home.

However, directly entering https://localhost:3000/home or https://www.myapp.io/home in the browser will raise an error (I don't know how to turn previous <h1><%= message %></h1><h2><%= error.status %></h2><pre><%= error.stack %></pre> in error.ejs to error.html, so I don't have more details).

So now, the goal is to make https://localhost:3000/home and https://www.myapp.io/home work.

By following this thread, I added the follows 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 in Apache of Mac, here is my httpd-vhosts.conf, after restarting apache, https://localhost:3000/home still returns 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, here is the NGINX server block. After restarting NGINX, https://www.myapp.io/home still returns an error.

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";     } } 

Could anyone help?

5 Answers

Answers 1

try this in 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 the <base href="/"> in your index.html

let me know if this works for you

EDIT:

I just found your app in https://github.com/chengtie/mini-mean, looks like your app.use order is wrong. Please copy paste this in your express server and check if it's okay now. pastebin

Answers 2

This might useful stuff,

AngularJS routing without the hash '#'

Also, use this line in your express server file. app.use(express.static(path.join(__dirname, 'client folder')));

this will directly finds your index.html file in that views folder and loads it

Answers 3

  1. You don't need Apache or Nginx to run NodeJs in development, just node server.js is enough
  2. Express gave you that error because you are using a deprecated API res.sendfile please use res.sendFile (capital F)
  3. Some info for doing SPA:
    • When you have the '#' in your URL, the browser interpret it as a local reference and thus, won't send new request to the server
    • By enabling $locationProvider.html5Mode(true) you are now using html5 push state to navigate around your application history and (if I'm not mistaken you are happening to be using) angular effectively remove the '#' in url
    • Without the '#' (hash-bang) browser will interpret it as a new request and send it to server, so you have to map all requests from server to your SPA entry file
    • For exact steps of replicating this behavior, consult this article: https://scotch.io/tutorials/pretty-urls-in-angularjs-removing-the-hashtag (the base href in your entry file is important)

Answers 4

var path = require('path');

app.use(express.static(path.join(__dirname, 'public'))); app.get('*', function(req, res){ res.sendFile(path.join(__dirname, './', 'views', 'index.html')); });

the above code solved my issue for mean stack

Answers 5

If it's just about the #. You can remove it in Angular itself.

Just inject locationProvider at your app entry and set htmlMode to true. In your index.html set the baseUrl.

$locationProvider.html5Mode(true) 

And in you index.html add:

<base href="/" /> 

That will generate your urls without the #. Does that help?

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment