If at any point, I get an error, by trying to do something with an undefined, my entire express app crashes with a TypeError, instead of handling it nicely by redirecting to an HTTP 500 page.
Is there any way to handle these exceptions generally, or I will need to manually check in my router methods?
Have you tried this from the docs - Express Error handling?
Error-handling middleware are defined just like regular middleware, however must be defined with an arity of 4, that is the signature (err, req, res, next):
app.use(function(err, req, res, next) {
console.error(err.stack);
res.send(500, 'Something broke!');
});
Though not mandatory error-handling middleware are typically defined very last...
Maybe try { ... } catch(e) {...} is what you are looking for?
Or just checks if value is not undefined. Simple if (value) { ... will do the job
As mentioned by phenomenal you can use express error handler middle-ware to handle error at centralized place.
app.use(function(err, req, res, next) {
console.error(err.stack);
res.send(500, 'Something broke!');
});
Than to pass error from your routes you can use next to pass error to this middle-ware like this
app.get('/login',function(req,res,next){
next("your error"); // The error middle-ware handle this error.
})
Related
I have an express sub app using http-errors module. When I pass new Forbidden() to the next() callback it disappears into the ether and doesn't callback. If I pass new Error() or { message: 'Forbidden' } it triggers the sub app error handler. Is this expected behaviour?
I've created my own Error objects and they all work. I see http-errors uses the inherits module, which works for me. Does the error handler check for anything on the error parameter?
I've used http-errors for years and not noticed this problem before.
const { Forbidden } = require('http-errors')
const express = require('express')
const app = express()
const subapp = express()
subapp.get((req, res, next) => {
next(new Forbidden()) // doesn't work
next(new Error()) // works
next({ message: 'Forbidden' }) // works
})
subapp.use((err, req, res, next) => {
// only called if new Error() or { message: 'Forbidden' }
})
app.use('/somepath', subapp)
app.use((err, req, res, next) => {
// not called
})
Edit:
I omitted that I was using swagger in the question above. Swagger was catching the error but not handling it appropriately; it was setting the correct headers but not sending a complete response. It was therefore missing my error middleware and passing on to the next non-error middleware.
// return response for http-errors error
subapp.use((req, res, next) => {
if (res.headersSent) {
return res.end()
}
next()
})
To answer the direct question, not it doesn't. We can test this with a simpler case shown in this code (tested).
const app = require('express')();
app.get('/test', (req, res, next) => {
const nonErrObj = {
hello: 'world'
};
return next(nonErrObj);
});
app.use((err, req, res, next) => {
console.log('Got an error');
console.table(err);
res.sendStatus(500);
});
app.listen(3000, () => {
console.log('listening on 3000');
});
By then running curl localhost:3000/test in another terminal we get the output:
listening on 3000
Got an error
┌─────────┬─────────┐
│ (index) │ Values │
├─────────┼─────────┤
│ hello │ 'world' │
└─────────┴─────────┘
This console.table is being output by our error handler, and the object we're passing to next is just a standard JS object. So the object passed to "next" can be anything and it will trigger the error handling code.
Now lets try and solve your issue. I have a hunch it's to do with your nested application which is good use of express but can get a bit confusing sometimes. I've created another test app using your code which shows the following. This code has only one global error handler.
const express = require('express');
const app = express();
const subapp = express();
// Create a top level route to test
app.get('/test', (req, res, next) => {
const nonErrObj = {
hello: 'world'
};
return next(nonErrObj);
});
// Create a sub express app to test
subapp.use('/test2', (req, res, next) => {
const nonErrObj = {
hello: 'world'
};
return next(nonErrObj);
});
// Mount the app, so we can now hit /subapp/test2
app.use('/subapp', subapp);
// A single global error handler for now
app.use((err, req, res, next) => {
console.log('Got an error');
console.table(err);
res.sendStatus(500);
});
app.listen(3000, () => {
console.log('listening on 3000');
});
If we now curl localhost:3000/subapp/test2 and curl localhost:3000/test we get the same response. Our global error handler is called with no problems. Now lets try adding an error handler to the sub app to see what happens.
In this case I just added the following under the /test2 route (not adding the full file for brevity.
subapp.use((err, req, res, next) => {
console.log('Sub app got error');
console.table(err);
res.sendStatus(500);
});
In this instance by doing the same thing, we can see that a request to localhost:3000/subapp/test2 only calls the the sub app error handler. This shows that the errors are being handled properly.
From the above we can see that there aren't any issues with random JS objects being passed through (you can dig through the Express Router code and see this as well). The only reason I can see that the http-errors wouldn't be working properly would be if they are causing a conflict with the error handling code.
Looking at the express router code we can see that it's picking up a few properties from the error object and acting based on that. I would check that your http-errors Forbidden error object isn't accidentally conflicting with one of these use cases. If that's the case, then I'd suggest finding a different error library.
I'm assuming you're also using the npm http-errors library. If that's the case, it looks like you should be providing a message on error creation. You could be getting yourself into a situation where your program is hanging or erroring in some other way because you're not providing a message.
I have node 7.8.0 and I have read Node.js Best Practice Exception Handling
Still I can't seem to understand error handling in node.js.
For my http I have
server.on('error', () => console.log('Here I should be replacing
the Error console.'));
For 404 I have this code
app.use((req, res, next) => {
let err = new Error('Not Found');
err.status = 404;
next(err);
});
app.use((err, req, res) => {
res.status(err.status);
res.render('error');
});
404 is being caught, it creates the Error object and then it's handled by rendering the error view correctly as I want. But it also throws error log to my console and I have no idea where that is coming from and how to stop it.
I would like to use my own error handling and never actually write errors like 404 into the console.
Codes that I tried and don't seem to work.
var server = http.createServer(app);
const onError = () => console.log('Here I should be replacing the Error console.');
server.on('error', onError);
server.on('uncaughtException', onError);
process.on('uncaughtException', onError);
I'm not sure how it can render the error view, given that error handlers require 4 arguments while you're passing it only 3. This is how Express can distinguish error handlers from normal route handlers (documented here).
Try this instead:
app.use((err, req, res, next) => {
res.status(err.status);
res.render('error');
});
This is due to expressjs. Expressjs if the env is not equal to the test, the all errors writes to the console.
function logerror(err) {
if (this.get('env') !== 'test') console.error(err.stack || err.toString());
}
Also http.Server does not have an event named error.
The typical errors I expect to see logged are not being logged in my express routes.
for example:
router.get('/', function(req,res,next){
console.log(undefinedVariable)
})
I expect to get an error:
ReferenceError: undefinedVarialbe is not defined;
however In the router.get I don't get any errors other than:
GET / 500 3.641 ms - 478
in my console which is making debugging hard. Is there any way to get these errors to show in the console?
At the end of your routes, add this:
router.use(function (err, req, res, next) {
if (err) {
console.log('Error', err);
} else {
console.log('404')
}
});
This will catch all routes that were not handled by a handler before it, and all routes that resulted in an error.
If you used express-generator, there should already be an 'error handler' in app.js
Just add this to the bottom of that handler:
if (app.get('env') === 'development') console.log(err.stack);
I have a node.js + Express + express-handlebars app. I want to redirect the users to 404 page when they go to a page that does not exists and redirect them to a 500 when there is an internal server error or an exception(without stopping the server). In my app.js I have written the middle ware at the end to perform these tasks.
app.get('*', function(req, res, next) {
var err = new Error();
err.status = 404;
next();
});
//Handle 404
app.use(function(err, req, res, next){
res.sendStatus(404);
res.render('404');
return;
});
//Handle 500
app.use(function(err, req, res, next){
res.sendStatus(500);
res.render('500');
});
//send the user to 500 page without shutting down the server
process.on('uncaughtException', function (err) {
console.log('-------------------------- Caught exception: ' + err);
app.use(function(err, req, res, next){
res.render('500');
});
});
However only the code for 404 works. So if I try to go to an url
localhost:8000/fakepage
it successfully redirects me to my 404 page. The 505 does not work. And for the exception handling, the server does keep running but, it does not redirect me to the 500 error page after the console.log
I am confused by so many solutions online where people seem to implement different techniques for this.
Here are some of the resources I looked at
http://www.hacksparrow.com/express-js-custom-error-pages-404-and-500.html
Correct way to handle 404 and 500 errors in express
How to redirect 404 errors to a page in ExpressJS?
https://github.com/expressjs/express/blob/master/examples/error-pages/index.js
The process on uncaughtexception is for the application process - not a per request error handler. Note it takes an err in it's callback and res is not passed. It's a global application exception handler. It's good to have that in the event your global code throws.
One option is you can have all your normal routes (don't see in your example), then a non error handler final * route for 404. That's always the last route meaning it has fallen through all the other routes not finding a match ... thus not found. It's not an exception handling case - you conclusively know the path they are requesting has no match since it has fallen through.
How to redirect 404 errors to a page in ExpressJS?
Then the err route can return 500
http://expressjs.com/en/guide/error-handling.html
The problem is you have two error routes so it always hits the first one which hard codes returning a 404.
The express 4 tool creates this pattern:
var users = require('./routes/users');
// here's the normal routes. Matches in order
app.use('/', routes);
app.use('/users', users);
// catch 404 and forward to error handler
// note this is after all good routes and is not an error handler
// to get a 404, it has to fall through to this route - no error involved
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers - these take err object.
// these are per request error handlers. They have two so in dev
// you get a full stack trace. In prod, first is never setup
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
I'm new to passport and express and trying to get a better idea of how everything works together by creating unit tests for all of my code. I have been having good success but today ran into an issue I don't quite understand...
I'm attempting to unit test the following exported function on session.js:
'use strict';
var mongoose = require('mongoose'),
passport = require('passport');
exports.login = function (req, res, next) {
passport.authenticate('local', function(err, user, info) {
var error = err || info;
if (error) return res.json(401, error);
req.logIn(user, function(err) {
if (err) return res.send(err);
res.json(req.user.userInfo);
});
})(req, res, next);
};
I seem to be having trouble with the second to last line (req, res, next) and how/when it gets called. I also have never seen this type of call in Javascript where the last line is just a set of parameters.
In order to test I'm using rewire, sinon, and mocha. In my test I'm using rewire to change the passport dependency to a mock object that I control.
My test seems to be working as expected as evidenced by where it is failing and what instanbul is telling me is covered but it alwasy throws: TypeError: undefined is not a function.
If I comment out the (req, res, next) line in session.js then my test actually runs successfully.
My test session.unit.js:
'use strict';
var rewire = require('rewire'),
should = require('should'),
sinon = require('sinon'),
sessionController = rewire('../../../../lib/controllers/session'),
passportMock = {};
describe('Session Controller', function() {
describe('#login', function(){
beforeEach(function(){
sessionController.__set__({
'passport': passportMock
});
});
it('should return 401 status and error if passport authentication returns error', function(){
//given
var res = {
json: sinon.spy(),
send: sinon.spy()
},
req = {
logIn: sinon.spy()
},
next = sinon.stub();
passportMock.authenticate = sinon.stub().callsArgWith(1, 'ERROR', null, null);
//when
sessionController.login(req, res, next);
//then
res.json.calledWith(401,'ERROR').should.be.true;
});
});
});
My questions are:
What is the last line (req, res, next) used for and when is it called? (I suspect middleware)
Is there a way I can change my test to get it to pass without the undefined error?
Thanks very much for reading and any help you can provide!!
** EDIT: **
I think I left out a critical piece of information... This session.login function is called from my routes.js file so I believe the last line is needed for middleware to continue. I'm still confused as to how to allow this to continue processing in the unit testing though.
routes.js snippet:
app.route('/api/session')
.post(session.login)
.delete(session.logout);
** EDIT2: **
Added the top part of session.js for clarity on how passport is initially declared with require. In the unit test I am replacing passport via rewire with my mock (stub) object so that I can control what passport returns.
I'm not sure if this will help, but this is generally how I stub passport.authenticate when used in the same way you have.
var stub = sinon.stub(passport, 'authenticate').returns(function() {});
stub.yields(new Error('fails here'));
// add your tests here
stub.restore();
The })(req, res, next); towards the end of your session.js is unnecessary because of how scoping rules work in JavaScript and because passport.authenticate does not return a function (it probably returns nothing which by default returns undefined), so that is the reason for the error.
Just change the })(req, res, next); from the end of session.js to just });.