Using Express 4 I'm struggling to get custom error handling middleware to work.
I'm using the NPM package express-async-errors to handle async errors, but my error handler isn't being hit for async or synchronous calls.
Routes defined like this in their own file:
module.exports.set = (app, apiVersion) => {
app.get(
`${apiVersion}/User/iscool`, UserTest.TheUserTestWorks,
);
app.get(
`${apiVersion}/User/error`, UserTest.TheUserTestThrowAnErrorSynchronously,
);
app.get(
`${apiVersion}/User/AsyncError`, UserTest.TheUserTestThrowAnErrorAsync,
);
};
And added in index.js like this:
fs.readdir("./routers", (err, routes) => {
routes.map((route) => {
const module = require(`./routers/${route.split(".").slice(0, -1).join(".")}`);
module.set(app, apiVersion);
return route;
});
return true;
});
Middleware defined like this:
const Sentry = require("#sentry/node");
function genericExceptionHandlerMiddleware(err, req, res, next) {
Sentry.captureException(err);
res.status(500).send("An unknown error occurred");
next(err);
}
module.exports = genericExceptionHandlerMiddleware;
And added like this at the bottom of Index.js immediately before module.exports = app:
app.use(genericExceptionHandlerMiddleware);
It does work however if I add the app.use inside the file where the routes listed above are defined, however I want this to apply to all routes.
As far as I can tell there's no other middleware that would be swallowing the exception.
The process console shows the exception being hit when it's added in index.js but doesn't do seem to run the code (if I add a console.log, it's not logged). It does log when added to the end of the file with the routes.
Frustratingly simple what the issue was - the function fs.readdir is async, so even though the app.use was called at the end it obviously wasn't being run after the various routers had been setup.
Related
I upgraded to Express 4 and have the following problem with error handling.
Before I used to have the code in app.js — after all the possible routes I had
var routes = require('./routes')
app.use(routes.notfound)
app.use(routes.error)
app.use(routes.badrequest)
And then inside the /routes/index.js I had:
exports.notfound = function(req, res) {
res.status(404).format({
html: function() {
res.render('404')
},
json: function() {
res.send({
message: 'We did not find what you were looking for :(',
})
},
xml: function() {
res.write('<error>\n')
res.write(
' <message>We did not find what you were looking for :(</message>\n'
)
res.end('</error>\n')
},
text: function() {
res.send('We did not find what you were looking for :(\n')
},
})
}
Now when I call for 404 elsewhere in the app (not in app.js) using res.send(404) I get the right 404 code response but I don't get to the part where it selects whether it shows html or json or text.
How do I do that?
You need to handle error catching differently, here is one way to do so:
Create a middleware after all of your routes that will catch errors you pass to it, the callback would take in an extra parameter containing details about the error:
app.use((err, req, res, next) => {
// Handle the error here
});
Whenever you want to render an error, you can use next in your routes to pass it to this middleware, and pass extra information you can use to decide how to handle the error. There is a module called http-errors that can create objects like that for you. Here is an example route:
const createError = require('http-errors');
app.get('/posts', (req, res, next) => {
// Logic...
if(/* some condition */) {
next(createError(404));
}
});
This will pass the error object created to your error handling middleware, and from there you can choose how to handle it.
To extend this, and to make it work better with asynchronous code, you can wrap your router's callbacks with a function that will make sure exceptions that get thrown are passed over to the error handling middleware, this comes in handy when working with async and await:
// Wrapper function to forward errors from async scopes
const wrap = fn => (...args) => fn(...args).catch(args[2]);
app.get('/posts', wrap(async (req, res) => {
// Logic...
await controller.get('posts'); // Promise rejections will get forwarded to middleware
}));
This also lets you just throw the error object instead of calling next.
I have a koa 2 server.
The following code are my middlewares:
// parse body
app.use( bodyParser() )
// serve static
app.use( serve( path.join(__dirname, '/public') ) )
// routes
app.use( routes )
// error middleware
app.use( async ctx => ctx.throw(500) )
Everything works well but my problem is that when I go to localhost:8000, where my server lives, in the console I see the following error:
InternalServerError: Internal Server Error
at Object.throw (/Users/work/Desktop/server/node_modules/koa/lib/context.js:91:23)
I'm suspecting that after static, the app is going to the next middleware, which is the error middleware.
PS. I'm using app.use( async ctx => ctx.throw(500) ), to call next() if I'm getting errors on the other routes.
Does anyone know how to fix this?
Thanks!
I'm suspecting that after static, the app is going to the next
middleware, which is the error middleware.
koa-static transfers control to the next middleware by design.
Your routes middleware also await to the next middleware.
So you get an error.
Does anyone know how to fix this?
It's hard to say what you are going to achieve in the first place.
Setting 500 manually is probably a wrong idea. There should be 404 like:
// 404 middleware
app.use(async ({response}, next) => {
if (!this.body) {
response.status = 404
response.body = "Not Found" // or use template
}
await next() // send control flow back (upstream)
})
For SPA (without SSR) you probably want this catch-all route to send APP layout instead. And move that 404 middleware to the beginning of the file (where it will take control on the second "bubbling" phase.
Make sure you checked this
use like, you add a middleware to handle your custom error properly...
// serve static
app.use(serve(path.join(__dirname, '/public')))
// error middleware
app.use(async(ctx, next) => {
try {
await next();
} catch (e) {
console.log(e.message);
ctx.body = e.message
} finally {}
})
// routes
app.use(router.routes()).use(router.allowedMethods());
router.get('/a', ctx => {
try {
ctx.body = "sadsa"
} catch (e) {
ctx.body = e
console.log(e);
} finally {}
});
app.use(ctx => ctx.throw(500))
app.listen(7000)
I am working a REST web application backend and I got some problem when linking my middleware together.
For example, the stack of the middlewares that each request has to go through is like [before1, service, after1], and here's the code for the middleware "before1", this is just the code I used for testing:
'use strict';
var express = require('express');
var router = express.Router();
router.use(function(request, response, next){
console.log('This is middleware BEFORE1');
var success = true
if (!success){
// Go the logging middleware underneath
next();
}
else{
// Go to the 'service' middleware
next('route');
}
})
router.use(function(request, response, next){
console.log('This is middleware LOGGING');
response.sendStatus(400);
response.end();
})
module.exports = router;
The above code is simply saying if the 'before1' succeeds, it should go straight to call 'service' middleware, and otherwise go to the logging middleware underneath and end the request. But my problem is that I can't figure out a way that it could skip the logging middleware, I searched and found next('route') could help, but it didn't work here. What have I missed?
Thanks in advance!
EDIT:
Or more preferably, it's the best if I can issue an error in any of my middleware and handle all types of errors properly using a error handler middleware.
The skeleton of my top level code is the following:
// An array of middleware to be executed asynchronously
operations = [before1, service, before2];
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
console.log('Handling error!!!!');
res.end();
// return next(err);
}
console.log('middleware get executed');
// no errors so pass control back to express
next();
});
But I am not sure How should change my middlewares accordingly in order to do that.
next is a node-style callback, which means fn(err, ..), so your next('route') will only work to invoke errorhandlers.
You can implement your series directly by supplying an array of functions to the route, and using an express error handler as the catch all (see http://expressjs.com/guide/error-handling.html)
I'm experimenting with KrakenJS, trying to build an basic API to understand things.
One thing I'm unsure of is the middleware, specifically the 404/500 error handling.
"fileNotFound": {
"enabled": true,
"priority": 130,
"module": {
"name":"path:./lib/exceptions/404"
}
}
This catches any 404 errros, and then I handle that myself in my own configuration. However, why is this fileNotFound thrown? Where is serverError thrown for 500 errors?
I would like to define my own files for other exceptions like a 403, however how would I get this to trigger a middleware?
As #HeadCode mentioned, definitely read up on meddleware to understand how middleware gets loaded a bit better.
That said, we have to go over a few things in order to make what's happening more clear.
Handling 404s
First, let's go over how one would typically register a 404 handler in a plain old express app.
Typically you'd have the final middleware in your middleware continuation chain just assume that, if we made it that far without bailing, we simply cannot find the resource. Here's an example:
var express = require('express');
var app = express();
app.get('/firstRoute', function handler(req, res) { res.send('found me'); });
app.get('/secondRoute', function handler(req, res) { res.send('found me'); });
app.use(function notFoundHandler(req, res, next) {
res.status(404).send('Route Not Found');
});
app.listen(8000, function onListen() { console.log('listening on 8000...'); });
Since routes are resolved in the order they're added in Express 4, as long as your 404 handler is last you can be certain no other route matched.
This pattern is briefly described in the Express FAQs.
Handling 500s
Now let's move on to 500s.
Express has the concept of an error-handling middleware (also described on the Express site). An error handling middleware requires an arity of 4 (i.e., takes four arguments) and that's the only difference. They're only executed if an error is signaled which is done by passing an object into your next call. Easier explained in code:
var express = require('express');
var app = express();
app.get('/firstRoute', function handler(req, res) { res.send('found me'); });
app.get('/secondRoute', function handler(req, res) { throw new Error('oops'); });
app.use(function notFoundHandler(req, res, next) {
res.status(404).send('Route Not Found');
});
app.use(function errorHandler(err, req, res, next) {
res.status(500).send('Broken. :(');
});
app.listen(8000, function onListen() { console.log('listening on 8000...'); });
In the above example, errorHandler will only execute if either 1) another middleware or route handler throws an error or 2) we call next with an argument*, .e.g. next(new Error('oops')).
Go ahead and run that. if you visit /notFound or any random route, you'll correctly get a 404. If you visit /firstRoute you'll get found me, and if you go to secondRoute you'll get Broken. :(.
What about kraken?
Kraken—or more accurately, meddleware—just moves defining your middleware into your config. That little block of json you copied above is basically functionally equivalent to the following in a vanilla express app:
var fileNotFound = require('./lib/exceptions/404');
// ... app.use() everything with a priority lower than 130 ...
app.use(fileNotFound());
// ... app.use() everything with a priority *greater* than 130 ...
I am trying to setup error handling for my express app and running into the following problem.
I defined an error middleware and add it as the last middleware:
// error handler
app.use(function(err, req, res, next) {
console.log('JUST TESTING. ERROR HANLDER HAS BEEN CALLED...');
next(err);
});
Now I would expect this middleware to be called whenever an error occurs:
app.get('/datenschutz', function(req, res, next){
return next(new Error('Just testing')); // handle everything here
});
However my middleware is never called! The browser does display the stack trace however.
This seems that there is another middleware that is catching this error and processing it before I can do anything about it.
The problem is that I have no clue where this middleware could be defined, as I have a very simple setup:
// setup ssl for local testing
var
app = express();
app.
use(express.static(__dirname + '/public')).
use(express.bodyParser()).
use(express.cookieParser());
Why is my error handling middleware not being called?
Where is this 'default' error handling taking place?
Thanks!
* EDIT *
I see that the middleware is indeed working. However this is the case if I call it from another middleware function.
However it is not being invoked if the error occurs inside a function defined as an express route (GET, POST, etc..). This is very strange.
If I add my error middleware to the route callbacks it then works:
app.get('/testError', function(req, res, next){
return next(new Error('Just testing')); // handle everything here
}, function(err,req,res,next) {
console.log('This error handler is called!!');
return next();
});
* EDIT 2 - FOUND ACCEPTABLE WORKAROUND **
I am surprised it has to be done this way. As I had read many entries/questions about error handling in express and never found this possibility mentioned.
However it seems that if an error ocurrs inside a route callback regular error middleware handlers will not pick it up. You will need to define an error handler at route level.
app.all('*', function(err,req,res,next) {
console.log('This is a global error handler at route level....');
return next(err);
});
I had this problem as well, but I couldn't figure out why it wasn't working even though I set my error handler after the app.user(app.router). As it turns out, I already had an error handler that I wasn't aware of.
Specifically, if you use the express cli to generate an app like I did, it will automatically add in this in:
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
Unfortunately for me, I added a bit more middleware to my app, which consequently obscured this statement and thus prevented my custom error handler from being called.
Simply remove that and then it should work properly.
On a side note, I should mention that the original solution still worked - even with the app.use(express.errorHandler()).
app.all('*', function(err,req,res,next) {
console.log('This is a global error handler at route level....');
return next(err);
});
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; For 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
});
"
EDIT 2 (sabtioagoIT) works. But just for those who missed it, emostar's solution also works.
I understood to move the error handling 'use' call to the end, but there seems to be an easier option as emoster suggests, use app.router (before the error handling 'use' call).
instead of making
app.get('/datenschutz', function(req, res, next){
return next(new Error('Just testing')); // handle everything here
});
you can install express-async-errors
and just make
app.get('/datenschutz', function(req, res){
throw new Error('Just testing');
});
it works as expected