Express and URL rewriting | HTML5 history - node.js

I'm trying to build a simple server to serve a single HTML page where all the logics are
handled by Angular. As far as I'm using the HTML5 history mode I'm able to navigate
through standard URLs.
Now, to make this work I need to enable URL rewriting. I tried with this bunch of lines and
although return always the correct HTML page, the URL vary and does not keep the initial
value. For example /popular should load index.html and leave the URL /popular so that the
JS logic can load the desired page.
Here follows the express code.
var express = require("express");
var app = express();
app.configure(function(){
app.use(express.static(__dirname + '/dist'));
});
app.get("/*", function(req, res, next){
res.sendfile(__dirname + '/dist/index.html');
});
app.listen(3000);
Any suggestion is appreciated.
Thanks everyone.

You need to set the root directory for relative filenames.
app.all('/*', function(req, res) {
res.sendfile('index.html', { root: __dirname+'/dist' });
});

Related

Calling Express by hand

In a node.js/express/socket.io application, how does one "call" express by hand to load/render the home page without saying app.use(blah). In other words, if I wanted to tell express to load index.html by hand instead of automatically.
var express = require('express'),
app = express(),
...
//app.use magically loads index.html when the browser hits 8080
app.use(express.static(path.join(__dirname, '../client/www'))); //index.html is in www
var port = process.env.PORT || 8080; //select your port or let it pull from your .env file
//===============PORT=================
http.listen(port, function () {
console.log('listening on: ' + port);
}
Where index.html is in www ? This doesn't work:
app.get('/', function(req, res){
res.sendfile('index.html', { root: __dirname + "/relative_path_of_file" } );
});
Nor this:
app.get('/', function(req, res){
res.render('/home/idf/Documents/js/react-trader/client/www/index.html', {user: req.user});
});
I was able to resolve the problem that is the cause of this question. I am using Passport to authenticate Express. I needed to protect the home page (index.html), so I added a route and ensured that the user had to be authenticated to view that page. So I said:
app.get('/index.html', ensureAuthenticated, function(req, res){
...
}
The problem is that if I do this, when the user authenticates, I couldn't figure out how to pass control to Express. I either could authenticate (and prevent accessing the home page) through Passport routing, or I could do Express. But I couldn't do both.
It turns out the answer is really simple (or at least in my very limited understanding at this point) I got to work by simply rewriting to
app.get('/index.html', ensureAuthenticated, function(req, res, next){
return next();
}
In essence, the way to break out of Passport routes and pass control to Express [or "call it by hand" - hence my question] is to return next();
This is not obvious at all, and it took quite a bit of experimentation to get it to work.

Express Static Middleware serves index.html automatically

I'm not very familiar with NodeJS and started to work with it and Express. Now I'm getting the following content: I want to serve an index.html file but make some other things before that. But since I'm using app.use(express.static(__dirname + '/client/public')); a browser request does not affect the app.get("/") function. How do I solve that problem?
app.use(express.static(__dirname + '/client/src/css'));
app.use(express.static(__dirname + '/client/public'));
app.get('/', function (req, res) {
console.log('###GET REQUEST received');
console.log(req);
res.sendFile(__dirname + '/index.html');
});
Thank you in advance!
Or, disable the indexing.
express.static(path, {index: false})
index: sends the specified directory index file; set to false to disable directory indexing.
from https://expressjs.com/en/4x/api.html#express.static
Order matters. Put your app.get route prior to the Express.static declaration.

Routing problems using NodeJS and AngularJS

I am building my first application using NodeJS & ExpressJS for the backend and AngularJS front end. I have all my front end routes working how I want, but I cannot figure out how to properly configure the Node backend so that when a specific URL is entered into the address bar, Node renders only the same Angular app every time.
For example, if I use About as a link to my about page in Angular, then the correct view will render and the address bar will show localhost:8080/about. However, if I just manually type in localhost:8080/about then Node responds with Cannot GET /about. Now I understand this is because my backend currently only handles the following:
var express = require('express');
var crypto = require('crypto');
var app = express();
app.set('views', __dirname + '/public');
app.engine('html', require('ejs').renderFile);
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
res.render('index.html');
});
// API
app.get('/api/sync', function(req, res){
// Here we generate a 32 byte string and make it the key
var num = Math.floor(Math.random()*100);
var key = crypto.createHash('md5').update(num.toString()).digest('hex');
key = key.slice(0, key.length/2);
console.log('Created key: ' + key);
res.send(key);
});
var server = app.listen(8080, function(){
console.log('Listening on port %d', server.address().port);
});
So what I want to do is make it so EVERY request to Node renders the same index.html page but then properly routes the view in Angular based on the URL. What is the best way to do this?
I just realized that using:
app.get('*', function(req, res){
res.render('index.html');
});
And placing this after all other routes I want to catch first will work.
Since I don't have enough reputation yet to just add a comment, it's worth noting that res.render() won't work if you're not using a server-side template rendering engine (as you are using EJS). You would instead want to use something like res.sendFile() if you were just serving a static HTML and Angular page with all the routing set up in Angular.
app.get( '*', function( req, res ) {
res.sendFile( __dirname + '/public/index.html' );
} );
The best way handle angular route in angular-app and backend route in backend.
Angular/Frontend:
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/home.html',
controller: 'MainController'
}).
when('/about', {
templateUrl: 'templates/about.html',
controller: 'AboutController'
}).
// >>> redirect other routes to
otherwise({
redirectTo: '/'
});
}]);
Backend:
For render static html you don't need app.get(...)
simple place index.html into:
public/index.html
and express serve it as html. Other not exists pages(routes) return 404 error and it is right.
In this case API fully separate and independent and angular fully single page app. Express serve static resources needed for angular.

Node.js catch-all route with redirect always renders index page with Angular regardless of url

Essentially when I use a catch-all route and use res.redirect('/') regardless of the url I enter it will always render the index/home page (ie Angular does not seem to 'see' the full url) however if I place res.render('index') in the catch-all route everything works fine. I don't want repeat code and redirecting to '/' should work, I have probably made a stupid mistake somewhere here and any help would be much appreciated!
Angular routing:
app.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/',
{
templateUrl: 'partials/home.jade'
})
.when('/about',
{
templateUrl: 'partials/about.jade'
})
.otherwise( {redirectTo: '/'});
});
This will correctly render the about page when entering site-address/about:
app.get('/', function (req, res) {
res.render('index');
});
app.get('/partials/:name', function (req, res) {
res.render('partials/' + req.params.name);
});
app.get('*', function (req, res) {
res.render('index');
});
This will always just show the index page:
app.get('/', function (req, res) {
res.render('index');
});
app.get('/partials/:name', function (req, res) {
res.render('partials/' + req.params.name);
});
app.get('*', function (req, res) {
res.redirect('/');
});
Configuration if it helps:
// Configuration
app.configure(function () {
app.set('port', process.env.PORT || 1337);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
});
This is by design.
When you use res.redirect('/'), Node/Express is sending an HTTP redirect, which will change the URL in the browser, thus when your index template is rendered, and when the angular code is run, the URL is /, regardless of what the user entered (the whole point of the redirect).
When you omit the redirect and just send the template as a response, NodeJs responds with an HTTP 200 (success) along with HTML content. Since the URL didn't change, when your application runs, the angular routing properly routes.
EDIT TO ADD: Address Comment
Rather than have two routes render the same template, I would get rid of the / route all together, and just have the catch-all render the index template, which will then turn control over to the Angular router.
Otherwise, I would consider splitting your routes conceptually: All your application routes are specifically sent to angular router, and you render static routes via nodejs, and use your catch all to render a more appropriate page for a missing or unknown resource (more helpful for your users).
You can use regex-like languages to specify a single handler:
app.get('/()|(about)|(contact)/',function(req,res) {/* handle */});
For folder structure:
root
-web.js
-dist/index.html
-dist/rest of the page
Just paste the following snippet to your web.js
nodeApp.use('/', express.static(__dirname + '/dist'));
nodeApp.get('/[^\.]+$', function(req, res){
res.set('Content-Type', 'text/html')
.sendfile(__dirname + '/dist/index.html');
});
I have a problem where when I use the catch-all routing, all of my static assets (stylesheets, javascript files etc) don't load:
app.use(app.router);
app.use(express.static(path.join(__dirname, frontend.app)));
// when this is removed, I can load static assets just fine
app.get('*', function(req, res){
res.render('main');
});
When the app.get('*', ...) part is removed, I can load the static assets just fine (i.e. I can type in 'examplejavascript.js' and see the javascript file. When it is there however, express catches the assets.
$locationProvider.html5Mode(true);
are you sure your requests are hitting the nodejs server. using this means, angularjs will try to search for urlmapping inside browser.
try by commenting
$locationProvider.html5Mode(true);

Is it possible to set a base URL for NodeJS app?

I want to be able to host multiple NodeJS apps under the same domain, without using sub-domains (like google.com/reader instead of images.google.com). The problem is that I'm always typing the first part of the url e.g. "/reader" in Express/NodeJS.
How can I set up an Express app so that the base URL is something.com/myapp?
So instead of:
app.get("/myapp", function (req, res) {
// can be accessed from something.com/myapp
});
I can do:
// Some set-up
app.base = "/myapp"
app.get("/", function (req, res) {
// can still be accessed from something.com/myapp
});
I'd also like to configure Connect's staticProvider to behave the same way (right now it defaults to serving static files to something.com/js or something.com/css instead of something.com/myapp/js)
The express router can handle this since 4.0
http://expressjs.com/en/api.html#router
http://bulkan-evcimen.com/using_express_router_instead_of_express_namespace.html
var express = require('express');
var app = express();
var router = express.Router();
// simple logger for this router's requests
// all requests to this router will first hit this middleware
router.use(function(req, res, next) {
console.log('%s %s %s', req.method, req.url, req.path);
next();
});
// this will only be invoked if the path ends in /bar
router.use('/bar', function(req, res, next) {
// ... maybe some additional /bar logging ...
next();
});
// always invoked
router.use(function(req, res, next) {
res.send('Hello World');
});
app.use('/foo', router);
app.listen(3000);
Previous answer (before express 4.0) :
The express-namespace module (dead now) used to do the trick :
https://github.com/visionmedia/express-namespace
require('express-namespace');
app.namespace('/myapp', function() {
app.get('/', function (req, res) {
// can be accessed from something.com/myapp
});
});
At the moment this is not supported, and it's not easy to add it on your own.
The whole routing stuff is buried deep inside the server code, and as a bonus there's no exposure of the routes them selfs.
I dug through the source and also checked out the latest version of Express and the Connect middleware, but there's still no support for such functionality, you should open a issue either on Connect or Express itself.
Meanwhile...
Patch the thing yourself, here's a quick and easy way with only one line of code changed.
In ~/.local/lib/node/.npm/express/1.0.0/package/lib/express/servers.js, search for:
// Generate the route
this.routes[method](path, fn);
This should be around line 357, replace that with:
// Generate the route
this.routes[method](((self.settings.base || '') + path), fn);
Now just add the setting:
app.set('base', '/myapp');
This works fine with paths that are plain strings, for RegEx support you will have to hack around in the router middleware yourself, better file an issue in that case.
As far as the static provider goes, just add in /mypapp when setting it up.
Update
Made it work with RegExp too:
// replace
this.routes[method](baseRoute(self.settings.base || '', path), fn);
// helper
function baseRoute(base, path) {
if (path instanceof RegExp) {
var exp = RegExp(path).toString().slice(1, -1);
return new RegExp(exp[0] === '^' ? '^' + base + exp.substring(1) : base + exp);
} else {
return (base || '') + path;
}
}
I only tested this with a handful of expressions, so this isn't 100% tested but in theory it should work.
Update 2
Filed an issue with the patch:
https://github.com/visionmedia/express/issues/issue/478
Just to update the thread, now with Express.js v4 you can do it without using express-namespace:
var express = require('express'),
forumRouter = express.Router(),
threadRouter = express.Router(),
app = express();
forumRouter.get('/:id)', function(req, res){
res.send('GET forum ' + req.params.id);
});
forumRouter.get('/:id/edit', function(req, res){
res.send('GET forum ' + req.params.id + ' edit page');
});
forumRouter.delete('/:id', function(req, res){
res.send('DELETE forum ' + req.params.id);
});
app.use('/forum', forumRouter);
threadRouter.get('/:id/thread/:tid', function(req, res){
res.send('GET forum ' + req.params.id + ' thread ' + req.params.tid);
});
forumRouter.use('/', threadRouter);
app.listen(app.get("port") || 3000);
Cheers!
I was able to achieve this using a combination of express-namespace for the routes and a fix from the below google group discussion for the static assets. This snippet will treat a request to /foo/javascripts/jquery.js like a request to /javascripts/jquery.js:
app.use('/foo', express.static(__dirname + '/public'));
Source:
https://groups.google.com/forum/#!msg/express-js/xlP6_DX6he0/6OTY4hwfV-0J
I know this is a very old question but Express has changed a lot since most these answers were posted so I thought I'd share my approach.
You can, of course, use Routers with Express 4 to group together related functionality behind a particular path. This is well documented and has already been covered by other answers.
However, it is also possible to mount an entire application at a particular path. As an example, let's assume our application (the one we want to host at /myapp) looks like this, in a file called myapp.js:
var express = require('express'),
path = require('path'),
app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/hello', function(req, res) {
res.send('Hello');
});
// Lots of other stuff here
exports.app = app;
In our main js file we could then mount this whole application at the path /myapp:
var express = require('express'),
app = express(),
myApp = require('./myapp').app;
app.use('/myapp', myApp);
app.listen(3000);
Note that we've created two applications here, one mounted on the other. The main application could have further sub-apps mounted at different paths as required.
The code in myapp.js is completely independent of where it was mounted. It's similar to the structure used by the express-generator in that regard.
Some documentation about sub-apps can be found here:
https://expressjs.com/en/4x/api.html#app.mountpath
https://expressjs.com/en/4x/api.html#app.onmount
There are also reliability issues. If reliability is important, a common solution is to use a front-end reverse HTTP proxy such as nginx or HAProxy. They both use single-thread evented architecture and are thus very scalable.
Then you can have different node processes for different subsites, and if one site fails (uncaught exception, memory leak, programmer error, whatever) the rest of sub-sites continue to work.
I was looking for this feature but for API routes, not for static files. What I did was that when I initialized the router, I added the mount path. So my configuration looks like this
//Default configuration
app.configure(function(){
app.use(express.compress());
app.use(express.logger('dev'));
app.set('json spaces',0);
app.use(express.limit('2mb'));
app.use(express.bodyParser());
app.use('/api', app.router); // <---
app.use(function(err, req, res, callback){
res.json(err.code, {});
});
});
Notice the '/api' when calling the router

Resources