I know there are other answers to this but they seem to have caveats.

This one causes a redirect, which can be fatal for my front-end app which uses Mixpanel, and a double-load of Mixpanel will a Maximum Call Stack Size Exceeded error in the browser.

This one uses sendFile which I personally cannot get to work. Trying to diagnose that, I'm just advised to use express.static().

Currently my code looks like this:

home.js - Some routes, the last one intended to be the front-end site.

var indexPath = path.resolve( __dirname, '../' + process.env.PUBLIC_FOLDER )

router.get( '/:user/:stream/:slug', function( req, res, next ) {

    if ( req.headers['user-agent'].indexOf( 'facebook' ) != -1 ) {
        // stuff to handle the Facebook crawler
    } else return next()
})

router.get( '/*', function( req, res ) {
    express.static( indexPath )
})

server.js - configuring node/express

app = express();

app 
    .use( morgan( 'dev' ) )
    .use(bodyParser.urlencoded( { limit: '50mb', extended: true } ) )
    .use( bodyParser.json( { limit: '50mb' } ) )
    .use( '/api', require('./routes/usersRoute.js') )
    .use( '/', require( './routes/home' ) )
    .on( 'error', function( error ){
       console.log( "Error: " + hostNames[i] + "\n" + error.message )
       console.log( error.stack )
    })

http
    .createServer( app ).listen( process.env.PORT )
    .on( 'error', function( error ){
       console.log( "Error: " + hostNames[i] + "\n" + error.message )
       console.log( error.stack )
    })

Some more info

The reason you can see I'm trying to use express.static() is because when I use res.sendfile() I get a problem like this one where the console says Unexpected token '<'. Unfortunately the answer doesn't specify an exact fix and neither does the questioner who says they fixed the problem but don't share an answer.

In my trial and error I have added some more to express, like this

.use( '/app/app.js', express.static( indexPath + '/app/app.js' ) )
.use( '/app/views', express.static( indexPath + '/app/views' ) )
.use( '/app/controllers', express.static( indexPath + '/app/views' ) )
.use( '/app/directives', express.static( indexPath + '/app/views' ) )
.use( '/app/vendor', express.static( indexPath + '/app/vendor' ) )
.use( '/js', express.static( indexPath + '/js' ) )
.use( '/css', express.static( indexPath + '/css' ) )
.use( '/fonts', express.static( indexPath + '/fonts' ) )
.use( '/images', express.static( indexPath + '/images' ) )
.use( '/api', require('./routes/usersRoute.js') )
.all( '/*', require( './routes/home' ) )

And in my home.js routes files added this

router.get( '/*', function ( req, res ) {
    res.status( 200 ).set( { 'content-type': 'text/html; charset=utf-8' } )
    .sendfile( indexPath + '/index.html' )
})

And in the browser I can see all my files are loading, but with the < error above. I see this /*/ route is being called hundreds of times when I do a refresh so I think the .use( '...', ... ) configurations are being ignored.


Here is another example requested by Jonas below.

var indexPath = path.resolve( __dirname, process.env.PUBLIC_FOLDER )

mongoose.connect( process.env.MONGOLAB_URI )

app = express();

app 
    .use( morgan( 'dev' ) )
    .use(bodyParser.urlencoded( { limit: '50mb', extended: true } ) )
    .use( bodyParser.json( { limit: '50mb' } ) )
    .use( '/api', require('./routes/usersRoute.js') )
    .use( '/', require( './routes/home.js' ) )
    .use( express.static( indexPath ) )
    .on( 'error', function( error ){
       console.log( "Error: " + hostNames[i] + "\n" + error.message )
       console.log( error.stack )
    })

I have also done the same without the .use( '/', require( './routes/home.js' ) ) line to try narrow down any problem, but it's the same result. The page will load if I have the # in the URL, but the browser will remove the # (so far so good). But if I press refresh, or put in the URL manually, sans-hashbang, it will give an error like Cannot GET /home/splash, where /home/splash is whatever path I'm going to.

share
    
How do I add a bounty to this? It's been 4 days... – Noah Jul 13 '15 at 22:21
    
I'm not an expert with express, but I've never see anyone go wrong when following the samples in the UI-Router FAQ, which shows using app.all rather than router.get. – Claies Jul 17 '15 at 23:49
    
Did you check the paths passed to express.static are valid? What is the value of indexPath? – manji Jul 21 '15 at 17:52
    
simply make var app = express(); app.configure(function() { ... app.use(express.static(__dirname + '/public')); ... } – Paweł Smołka Jul 21 '15 at 19:36
    
@PawełSmołka I don't think app.configure is part of express 4. – Noah Jul 22 '15 at 19:24
up vote 11 down vote accepted
+25

You might be using the express middleware in the wrong way. Looking at the documentation tells me for example that the following code

.use( '/images', express.static( indexPath + '/images' ) )

Serves any file under the folder indexPath + '/images' with such URLs:

http://localhost:3000/images/kitten.jpg
http://localhost:3000/images/logo.png
http://localhost:3000/images/whatever.jpg

Is that what you would expect it to do?

My suggestion is to change from

.use( '/', require( './routes/home' ) )

to

.use(express.static( indexPath ))

Because according to the documentation, it will serve all static files under the indexPath folder from the root path, aka. with no prefix.

I think that is all I can help with the input you gave so far. If that doesn't do the trick, maybe you can share some small code example that I can use to reproduce it on my own.

UPDATE

Ok. I've tried to create a simple example of it. I made it work in the following way. My directory structure is like this:

|- index.js
|- public/
|---- index.html
|---- test.html
|---- main.js
|---- angular.js
|---- angular-route.js

index.js is the node server. Pay attention to the order of the routing. I also added some /home/login example to make it clear how to add other server routes that should not go through angular.

var http = require('http');
var express = require('express');
var app = express();

app
    .use(express.static('public'))
    .get('/home/login', function (req, res) {
        console.log('Login request');
        res.status(200).send('Login from server.');
    })
    .all('/*', function ( req, res ) {
        console.log('All');
        res
            .status( 200 )
            .set( { 'content-type': 'text/html; charset=utf-8' } )
            .sendfile('public/index.html' );
    })
    .on( 'error', function( error ){
       console.log( "Error: \n" + error.message );
       console.log( error.stack );
    });

http
    .createServer( app ).listen( 8080 )
    .on( 'error', function( error ){
       console.log( "Error: \n" + error.message );
       console.log( error.stack );
    });

console.log('Serving app on port 8080');

index.html is pretty simple.

<!doctype html>
<html>
<head>
    <title>Angular HTML5 express test</title>
    <script type="text/javascript" src="angular.js"></script>
    <script type="text/javascript" src="angular-route.js"></script>
    <script type="text/javascript" src="main.js">
    </script>
</head>
<body ng-app="app">
    <div ng-view></div>
</body>
</html>

main.html just adds some content. Important is the link to /test

<div>
    <h1>This is just a {{val}}</h1>
    <button ng-click="clicked()">Click me!</button>
    <span>You clicked {{counter}} times</span>
    <a href="/test">test me!</a>
</div>

test.html is actually irrelevant

<div>
    <h4>What a beautiful day to test HTML5</h4>
</div>

main.js is doing the core angular html5 work

angular.module('app', ['ngRoute'])
    .config(function($locationProvider) {
        $locationProvider
            .html5Mode({
                enabled: true, // set HTML5 mode
                requireBase: false // I removed this to keep it simple, but you can set your own base url
            });
    })
    .config(function($routeProvider) {
        $routeProvider
            .when('/test', {templateUrl: 'test.html', controller: function() {
                console.log('On /test.');
            }})
            .when('/', {templateUrl: 'main.html', controller: 'MyTestCtrl'})
            .otherwise('/');
    })
    .controller('MyTestCtrl', function ($scope) {
        self = $scope;
        self.val = 'TeSt';
        self.counter = 0;
        var self = self;
        self.clicked = function() {
            self.counter++;
        };
    });
share
    
When I use express.static with HTML5 mode in Angular, I get the error Cannot GET [path to page] after refreshing or manually entering a URL. If I add the # back to the URL, I can view the page and the # will have been removed. – Noah Jul 22 '15 at 18:39
    
@Noah Can you create an example? – Jonas Hartmann Jul 24 '15 at 7:47
    
Are you telling me you know for sure that .use(express.static( indexPath )) supports HTML5? Because it doesn't seem to. It works until I hit refresh, then it says Cannot GET /home/splash. If I put the # back in the URL, it loads and removed the #, but refreshing again gives the error again. – Noah Jul 24 '15 at 20:45
    
I've appended an example above. – Noah Jul 24 '15 at 22:16
1  
I think the issue was that I didn't put .use(express.static('public')) first, I always put it last. – Noah Aug 7 '15 at 20:24

instead of:

router.get( '/*', function( req, res ) {
    express.static( indexPath )
})

do

router.get( '/:anyreq', function( req, res ) {
    express.static( indexPath )
})

just keep it at end of routes file.

share
    
I get Cannot GET /home/login when using a path and Cannot GET / when using a # in the path. – Noah Jul 24 '15 at 21:11

It seems like there might be a problem with your AJAX requests.
I usually set up my routes like this:

app.use(express.static(path.join(__dirname, "./public"))); app.use("/", require(path.join(__dirname, "./routes")));

and make calls on the front end with templateUrl: "/templates/home.html" in a directive

or $http.get("/images").success(...).error(...) using $http.

In the templateUrl case the app will go into the public directory, then the path templates and serve the html file. In the $http case I'm specifying a route so the app checks the public directory, doesn't see an images path and so moves on to the router. In the router I have a router.get("/images", function (req, res, next) { res.sendFile(...); or res.send({(data)}) }) which sends the file I need back to the front end where it is caught in the success handler.

share
    
I am using $http on the front-end and declaring the templateUrl like you explain. Are you using html5Mode? Can you confirm it works with Express? – Noah Jul 24 '15 at 22:01
    
I'm using angular and express with that set up. In the static router you need to provide the relative path (ie path.join(__dirname, "public")) – Isaac Madwed Jul 24 '15 at 23:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.