How do express static directories work with a 404 route? - node.js

I have some code that looks like the following:
app.configure(function() {
app.set("views", __dirname + "/views");
app.set("view engine", "ejs");
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.logger());
app.use(app.router);
app.use(express.static(__dirname + "/public"));
});
//Routes
app.get("/", function(req, res) {
res.render("index.ejs", {locals: {
title: "Welcome"
}});
});
//Handle 404
app.get("/*", function(req, res, next) {
next("Could not find page");
});
The problem I have is that I can't access anything in the /public static directory: everything gets caught by the 404 route. Am I missing something about how this is supposed to work?

You're doing
app.use(app.router);
app.use(express.static(__dirname + "/public"));
What you want to do is
app.use(express.static(__dirname + "/public"));
app.use(app.router);
Since you have a catch all route in app.router it must be lower then anything else. otherwise the catch all route will indeed catch everything and the rest of the middleware is ignored.
As an aside catch all routes like that are bad.

A better solution would be to place the following code after all calls to app.use:
app.use(function(req, res) {
res.send(404, 'Page not found');
});
Or a similar function.
Do this instead of using app.get("/*", ...

I'm doing it a slightly different way. If you look at the middleware code for the static file server it allows for a callback function that gets called with errors. Only catch is you need the response object to send something useful back to the server. So I do the following:
var errMsgs = { "404": "Dang that file is missing" };
app.use(function(req, res, next){
express.static.send(req, res, next, {
root: __dirname + "/public",
path: req.url,
getOnly: true,
callback: function(err) {
console.log(err);
var code = err.status || 404,
msg = errMsgs["" + code] || "All is not right in the world";
res.render("error", { code: code, msg: msg, layout: false});
}
});
});
Basically what happens is if there is an error it renders my pretty error page and logs something so that I can debug somewhere.

Related

Node.js - res.sendFile - Error: ENOENT but the path is correct

I'm trying to render an index.html but I get the error enoent, even with the right path.
//folders tree
test/server.js
test/app/routes.js
test/public/views/index.html
//routes.js
app.get('*', function(req, res) {
res.sendFile('views/index.html');
});
//server.js
app.use(express.static(__dirname + '/public'));
require('./app/routes')(app);
I also tried
res.sendFile(__dirname + '/public/views/index.html');
If I use
res.sendfile('./public/views/index.html');
then it works, but I see a warning that says sendfile is deprecated and I have to use sendFile.
Try adding :
var path = require('path');
var filePath = "./public/views/index.html"
var resolvedPath = path.resolve(filePath);
console.log(resolvedPath);
return res.sendFile(resolvedPath);
This should clear up whether the file path is what you expect it to be
Try using the root option, that did it for me :
var options = {
root: __dirname + '/public/views/',
};
res.sendFile('index.html', options, function (err) {
if (err) {
console.log(err);
res.status(err.status).end();
}
else {
console.log('Sent:', fileName);
}
});
you can try below code
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.static(path.join(__dirname, 'public/views')));
Handle api call
app.use('/', function(req, res, next) {
console.log('req is -> %s', req.url);
if (req.url == '/dashboard') {
console.log('redirecting to -> %s', req.url);
res.render('dashboard');
} else {
res.render('index');
}
});
The problem is that you've defined static file middleware, but then you define a route in front of that that tries to handle serving the static files (so the static file middleware actually does nothing here). So if you want to res.sendFile something from a route you would need to give either an absolute path to it. Or, you could just remove the app.get('*', ...) route and let the express middleware do its job.

express 3 routes being called unnecessarily

I have a 2 routes, the first for an API and the second is a catch all which shows an html page. Now, even if I make a request to .../api/... via a browser, and see the "Invalid" - the second route, ie "called unnecessarily" still gets executed. I'm confused as to why the second route is called since it's already gone via the first route.
// api route
app.get("/api/appname/stuff_settings/:setting", function(req, res) {
// do api stuff
res.send(500, 'Invalid');
});
app.engine('.html', require('ejs').__express);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
// catch all route
app.use(express.static(path.join(__dirname, 'public')));
app.get('*', function(req, res){
res.render('index', {
console.log("called unnecessarily");
});
});
Do console.log(req.url); in the catch all route my money is on favicon.ico.

express global middleware not being called

As far as I can tell I'm configuring my global middleware function as described in the docs and in every forum post on the subject, but it is not being called. Does anyone see what I'm doing wrong? express 3.2.5. In the log output I see the following:
Express server listening on port 9000
inside route
GET / 200 7ms - 2b
I expect to see "inside middleware", then "inside route". Instead, I just see "inside route".
The code:
var express = require('express'), http=require('http'), path=require('path');
var app = express();
app.enable('trust proxy');
app.set('port', process.env.PORT || 9000);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.set('layout', 'layout');
app.use(require('express-ejs-layouts'));
app.use(express.favicon(__dirname + '/public/images/favicon.ico'));
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride())
app.use(express.cookieParser('kfiwknks'));
app.use(express.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
if ('development' == app.get('env')) {
app.use(express.errorHandler());
} else {
app.use(function(err, req, res, next){
console.error (error);
res.send (500, "Internal server error");
});
}
app.use (function (req, res, next) {
console.log ("inside middleware");
next();
});
app.get ("/", function (req, res) {
console.log ("inside route");
res.send(200);
});
http.createServer(app).listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
This related post:
Express 3 error middleware not being called
is specific to error handling middleware. Mine is a vanilla middleware.
You should put your middleware before you use app.router.
...
app.use (function (req, res, next) {
console.log ("inside middleware");
next();
});
...
app.use(app.router);
Updated answer for Express 4 users from the Express 4 docs. See example from docs below. Note that app.router is deprecated and no longer used. I also added a dummy route to make the ordering clear.
You define error-handling middleware last, after other app.use() and routes calls
Example:
var bodyParser = require('body-parser');
app.use(bodyParser());
app.get('/', function(req, res) {
res.send('hello world');
})
app.use(function(err, req, res, next) {
// logic
});

nodejs equivalent of this .htaccess

Is it possible to build a code like this in node.js?
<IfModule mod_rewrite.c>
     RewriteEngine on
     RewriteCond% {REQUEST_URI}! / (View) / [NC]
     RewriteCond% {REQUEST_FILENAME}!-F
     RewriteRule ^ (. *) $ Index.html [L, QSA]
</IfModule>
url display a route is not "view" and also the file does not exist then write index.html.
using something like express or connect
UPDATE: I need a regular expression for !/(view)/ in route for express in node.js.
Have you tried:
Serve statics
Catch /view URL
Catch everything else
app.configure(function(){
app.use(express.static(__dirname+'/public')); // Catch static files
app.use(app.routes);
});
// Catch /view and do whatever you like
app.all('/view', function(req, res) {
});
// Catch everything else and redirect to /index.html
// Of course you could send the file's content with fs.readFile to avoid
// using redirects
app.all('*', function(req, res) {
res.redirect('/index.html');
});
OR
Serve statics
Check if URL is /view
app.configure(function(){
app.use(express.static(__dirname+'/public')); // Catch static files
app.use(function(req, res, next) {
if (req.url == '/view') {
next();
} else {
res.redirect('/index.html');
}
});
});
OR
Catch statics as usual
Catch NOT /view
app.configure(function(){
app.use(express.static(__dirname+'/public')); // Catch static files
app.use(app.routes);
});
app.get(/^(?!\/view$).*$/, function(req, res) {
res.redirect('/index.html');
});
The final structure is:
var express = require('express'), url = require('url');
var app = express();
app.use(function(req, res, next) {
console.log('%s %s', req.method, req.url);
next();
});
app.configure(function() {
var pub_dir = __dirname + '/public';
app.set('port', process.env.PORT || 8080);
app.engine('.html', require('ejs').__express);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.static(pub_dir));
app.use(app.router);
});
app.get('/*', function(req, res) {
if (req.xhr) {
var pathname = url.parse(req.url).pathname;
res.sendfile('index.html', {root: __dirname + '/public' + pathname});
} else {
res.render('index');
}
});
app.listen(app.get('port'));
Thanks everyone.
PD: Render html with module ejs
I would recommend using the express Middleware urlrewrite.
If you don't handle rewrites on a reverse proxy for example and if using express and want regex support for flexibility use: https://www.npmjs.com/package/express-urlrewrite

Redirect loop with node express.js

I have simple webpage with /about, /contact, /home and /lessons routes defined. All routes work okay except for /lessons. I instantly get a redirect loop (Error 310 (net::ERR_TOO_MANY_REDIRECTS): There were too many redirects).
Here's my main server.js code :
var port = process.env.PORT || 8888;
var app = require('./app').init(port);
var markdown = require('./markdown');
var lessons = require('./lessons.json').lessons;
// app.use(function(req,res,next) {
// console.log('adding lessons to locals');
// res.locals.date = new Date().toLocaleDateString();
// res.locals.lessons = lessons;
// next();
// });
// app.use(app.router);
app.get('/', function (req, res) {
console.log('controller is : home');
res.locals.controller = 'home';
res.render('home');
});
app.get('/:controller', function (req, res, next) {
var controller = req.params.controller;
console.log('controller is : '+ controller);
if (controller == 'about' || controller == 'contact') {
res.locals.controller = controller;
res.render(controller);
} else {
console.log('next was taken!');
next();
}
});
app.get('/lessons', function(req, res) {
res.locals.lessons = lessons;
console.log('controller is : lessons');
res.render('lessons');
});
app.get('/lessons/:lesson', function(req, res) {
console.log('controller is : lesson');
res.locals.controller = 'lessons';
res.send('gimmie the lesson');
});
/* The 404 Route (ALWAYS Keep this as the last route) */
app.get('/*', function (req, res) {
console.log('got 404 request to ' + req.url);
res.render('404');
});
and here's the app.jsfile which is used for server initialization:
var express = require('express');
var slashes = require('connect-slashes');
exports.init = function (port) {
var app = express();
app.use(express.static(__dirname + '/public'));
// add middleware to remove trailing slash in urls
app.use(slashes(false));
app.set('views', __dirname + '/views')
app.set('view engine', 'ejs');
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.logger());
app.enable("jsonp callback");
if ('development' == app.get('env')) {
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
app.use(express.logger({
format: ':method :url'
}));
}
if ('production' == app.get('env')) {
app.use(express.errorHandler());
}
app.use(function (err, req, res, next) {
console.log('Oops, something went wrong');
res.render('500.ejs', {
locals: {
error: err
},
status: 500
});
});
app.listen(port);
console.log("Listening on port %d in %s mode", port, app.settings.env);
return app;
}
I have tried debugging the app with node-inspector but it's useless since the app doesn't seem to go into any of the app.gets to try to match. It immidiately gives me the error when I try to access localhost:8888/lessons
EDIT:
I think I have found the root of the problem :
My /public dir has a lessons folder
My /views dir has a lessons.ejs view
When I change /public/lessons into /public/lessons11 for example, the problem is resolved. Can someone explain to me what's express flow in the original scenario that causes the redirect loop ? also, what can I do to resolve it ?
Thanks
This happens:
a request for /lessons comes in;
the static middleware sees the public/lessons folder and assumes that's what the intended target is; because it's a folder, it will generate a redirect to /lessons/ (see below);
static middleware picks that request up again, but notices there's no index.html in that folder, and hands it off to the next middleware (connect-slashes);
the connect-slashes middleware removes the trailing slash and issues a redirect to /lessons;
the whole loop starts again;
You can prevent the static middleware from adding a trailing slash, which will fix your redirect loop I think:
app.use(express.static(__dirname + '/public', { redirect : false }));
You can try using express-redirect-loop middleware. It uses sessions and you can read more about it and implement it at https://github.com/niftylettuce/express-redirect-loop.

Resources