Error-handling middleware doesn't always work - node.js

I'm building a example website using Express and I hit something I don't quite understand.
Error-handling middleware(s) should be last one(s) in the pipeline, if I understand correctly. For example, works just fine:
var http = require('http');
var express = require('express');
var app = express();
app.set('view engine', 'jade');
app.set('views', './views');
app.use(express.static('./public'));
http.createServer(app).listen(portNumber, function() { });
app.get('/hello', function(req, res) {
res.send('Welcome!');
});
app.use(function(err, req, res, next) {
res.status(500).send('something broke!');
});
app.get('/error', function(req, res, next) {
somethingNonExistent(2016);
});
However, if I register that middleware before http.createServer call, but after all other middlewares were registered, it won't work - my code isn't called:
var http = require('http');
var express = require('express');
var app = express();
app.use(express.static('./public'));
app.use(function(err, req, res, next) {
res.status(500).send('something broke!');
});
http.createServer(app).listen(portNumber, function() { });
app.get('/hello', function(req, res) {
res.send('Welcome!');
});
app.get('/error', function(req, res, next) {
somethingNonExistent(2016);
});
What did I miss here? My guess is that app.get calls use some middleware internally and it gets messed up. I use Express 3.2.6 and Node.js 0.10.29, if that makes any difference

When you define routes/middleware, the path you specify is used to see if it matches with incoming requests. Your request will always be routed to the first match. A request might have multiple matches, so order matters here. You can hit the next matching route/middleware by calling the next() function.
When you mount middleware using app.use without specifying a path, every path is eligible to hit that middleware. So, if it's the first thing you mount, every request will use that middleware.
If you want a catch all error handler, you'll want the opposite - you should mount middleware at the very end of your route definitions. You'll need to call the next function in your handler to actually reach this middleware:
app.get('/hello', function(req, res, next) {
...
// Let's pretend that there was some error
next()
});
// After all of your route definitions...
app.use(function(req, res) {
res.status(500).send('something broke!');
})
Note that if no route exists for the current path, you will also hit this catch all middleware.

Docs
http://expressjs.com/en/guide/error-handling.html
You define error-handling middleware last, after other app.use() and
routes calls; for example:
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
// logic
});
The Internal
For ease of understanding, you can imagine the pipeline. When your controller receives a request, inside Express, it looks like this
try {
fn(req, res, next); // your controller
} catch (err) {
next(err);
}
So, then your code throw error, it will call next with err. Basically, it's the same as call next(new Error()). After that, express trying to find next middleware with 4 arguments in middleware pipeline. In your situation error handler declared before your shady controller, so he doesn't involved.
Technically, there is no difference between the controller and middlewares. Optionally you can pass in the controller next parameter , and call it to pass through the pipeline further. If you don't call next(), you finish processing the request.

Related

node express folder access for authorized users

I am trying to allow users to save and download static files. But have the files be accessible only for logged in (authorized) users.
routes/files.js
var express = require('express');
var router = express.Router();
/* Get files */
router.get('/*', isLoggedIn, function(req, res, next) {
console.log('get files called ');
express.static(path.join(__dirname, 'files'));
});
function isLoggedIn(req, res, next){
if(req.isAuthenticated()) return next();
res.redirect('/');
}
module.exports = router;
This works with any get request but not static files. They can be accessed with out being logged in.
app.js
var filesRouter = require('./routes/files');
app.use(express.static(__dirname));
app.use('/files', filesRouter);
I must be missing something simple - Thanks!!
Calling express.static(path.join(__dirname, 'files')); just returns a middleware function. It doesn't actually execute anything more than that. It doesn't serve any files until you call the function that it returns. You could get that middleware function once and then execute it conditionally, but it's easier to just do this:
/* Get files */
router.get('/*', isLoggedIn, express.static(path.join(__dirname, 'files')));
You should use express.static as a middleware.
And then to only allow authenticated users to access files, you coulld register isLoggedIn middleware before it. Please , see the code bellow, this should work.
var express = require('express');
var router = express.Router();
router.use(isLoggedIn); // this must come before express.static
router.use(express.static(path.join(__dirname, 'files')));
/* Get files */
router.get('/*', function(req, res, next) {
console.log('get files called ');
});
function isLoggedIn(req, res, next){
if(req.isAuthenticated()) return next();
res.redirect('/');
}
module.exports = router;

middlewares not running in Express

Based on Express docs, a middleware must run each time app is launched following this code:
var app = express();
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
Well, trying to execute with the most simple possilbe example middleware never is excuted:
var express = require('express');
var middleware = require('./middleware');
var app = express();
app.use(function (req, res, next){
console.log('MIDDLEWARE');
next();
});
module.exports = app;
Middleware never runs.
Also tryed to make it working from a separated file, but never runs.
Thanks
Middleware are lunch when there are any request to the server.
Create a route and send a request to that, the middleware would be lunched.
Ups, seems to be they're launched when recieves a request. So using Postman it worked.

custom express.js mounting does not work

I have trouble implementing route mounting in express.js 4.13.3.
When I first install it, by default created in the app.js
var users = require('./routes/users');//get the "users" route
app.use('/users', users);//mount to "/users"
and the users.js route is like
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.send('respond with a resource');
});
module.exports = router;
Does not need to define router.get('/users'... because mounting took care of that in the app.js file.
But
When I try to do the same thing
in app.js I set
var upload = require('./routes/upload');
app.get('/upload', upload);//mounting (?)
the upload.js route is
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.render('upload', {title: 'Photo upload'});
});
module.exports = router;
When I access localhost/users I get 404 error. The only way to fix this , is to define in the upload.js router, this router.get('/upload' instead of this router.get('/'. But that would not be mounting.
The difference I see is that the default code uses app.use('/users', users); and my code uses app.get('/upload', upload);. Is the verb (use/get) the only difference that causes the 404? And if so, why? Or is it something else?
Thanks
You are totally correct that the problem is caused because these to functions work differently. Below are the official API specifications for the functions.
app.use is for mounting a middleware
app.get is for defining (only) one route for a HTTP GET request
This example shows a middleware function mounted on the /user/:id path. The function is executed for any type of HTTP request on the /user/:id path.
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
This example shows a route and its handler function (middleware system). The function handles GET requests to the /user/:id path.
app.get('/user/:id', function (req, res, next) {
res.send('USER');
});

using Router in express-4

i am trying to execute below code after upgrading express4
// call the Router
var router = express.Router();
router.get('/test1', function(req, res, next) {
// doing more stuff
res.send('test test1 route')
});
// call our router we just created
app.use('/dogs', dogs);
but for some reason i am getting following error
this._router = new Router({
^
TypeError: object is not a function
at Function.app.lazyrouter
can someone help me to solve this problem ,Thank you in advance.
From the documentation:
var app = express();
app.route('/events')
.all(function(req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function(req, res, next) {
res.json(...);
})
.post(function(req, res, next) {
// maybe add a new event...
})
So try:
var router = app.route();
router.get('......
Or check the 3 to 4 upgrade guide.
Which says:
app.router has been removed and middleware and routes are executed in
the order they are added. Your code should move any calls to app.use
that came after app.use(app.router) after any routes (HTTP verbs).
app.use(cookieParser());
app.use(bodyParser());
/// .. other middleware .. doesn't matter what
app.get('/' ...);
app.post(...);
// more middleware (executes after routes)
app.use(function(req, res, next);
// error handling middleware
app.use(function(err, req, res, next) {});
I don't have a reputation to comment. Where and how did you declare your dog? Did you mean the following?:
// call the Router
var router = express.Router();
// call our router we just created
app.use(router);
router.get('/test1', function(req, res, next) {
// doing more stuff
res.send('test test1 route')
});
we can use this approach for routing in express4, when i am upgrading to express4 i didn't deleted express 3 folder, i tried deleting express3 folder from node modules and installing express4 than this worked fine

ExpressJS App.use Sequence of Routes

I have the following in app.js:
app.use(express.static(path.join(__dirname, 'public')));
app.use(function(req, res, next) {
console.log('app.use');
...
next();
});
app.use(app.router);
public folder have subfolder images, css and js.
After app.use(app.router);, I have route definition for returning an image from Amazon S3.
app.get('/images/:type/:id', image);
The problem is that when a page contain image, app.use was called twice. How to prevent it? I want to ignore /images/* calling app.use(function(req, res, next) {});
In general Express uses middleware connect architecture. Every middleware accepts next function which if is called passes the flow to the next middleware. So, in theory if you miss it you could archive what you want:
app.use(function(req, res, next) {
// check if the route matches amazon file
if(...amazon file...) {
// serve image
} else {
next();
}
});
app.use(express.static(path.join(__dirname, 'public')));

Resources