Hi I'm developing my first Node.js application and I found my first issue in something really basic, I can't catch the HTTP errors (404, 500, ...)
I've followed the doc (I think so) but I suppose that I'm missing something.
This is my code:
var express = require('express')
app = express()
app.use(express.logger('dev'));
app.get('/', function(req, res) {
res.send('Hellow World!!!')
})
app.use(app.router);
app.use(function(err, req, res, next) {
console.error('Oh!! ERROR');
console.error('My ERROR: ' + JSON.stringify(err));
});
app.listen(3000);
console.log('Server running at http://127.0.0.1:3000/');
Thanks in advance.
So the way express works is if it hits the end of the entire stack of middleware and route handlers and nothing has responded yet, it's going to send the fallback 404 response automatically without raising an error. This is why your error handling middleware isn't being called. By default, express doesn't consider a 404 an error, which is probably the right way to think of it.
If you want to customize this, you can put a middleware at the end of your stack to either serve a custom 404 page directly or call next with an error if you prefer that way.
app.get('/'...
app.get('/two'...
app.get(/'three'...
app.use(middleware1...
app.use(middleware2...
app.use(app.router);
app.use(function (req, res, next) {
//Option 1: respond here
return res.status(404).render('my_404_view');
//Option 2: trigger error handler
return next(new Error('404ed!'));
}
Option 2 will trigger your error handling middleware. There you will have to get info from the error object to determine the desired view to render and status code to send. I personally define an errors.NotFound class that has a 404 code and lets me render a friendly 404 page view.
Related
I have an Express server running for the backend of a website with Sentry (v5.15.5) successfully implemented. I'm now trying to improve the error handling on the backend, as at the moment if something goes wrong with the request, the request is not ended and the client sits there waiting for a response, which it never gets.
To tell the client if the request has failed I'm using the custom error handler in the documentation:
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
// and optionally displayed to the user for support.
res.statusCode = 500;
res.end(res.sentry + "\n");
});
However, when I use this on my server, only the custom error handler runs - Sentry never creates an event for the error, but if I just use another custom error function, they both get called fine, which makes me think this is a Sentry issue. Here's the relevant parts of the server code:
...
const Sentry = require('#sentry/node');
...
const app = express()
Sentry.init({ dsn: process.env.SENTRY });
...
// Middlewares
app.use(Sentry.Handlers.requestHandler());
app.use(express.json())
app.use(helmet())
app.use(cors())
app.use(morgan('tiny'))
const controllers = require('./controllers')
const wrap = fn => (...args) => Promise
.resolve(fn(...args))
.catch(args[2])
// Routes
...
app.post('/test', authUser, wrap(controllers.testController))
...
app.use(Sentry.Handlers.errorHandler());
app.use(function onError(err, req, res, next) {
res.statusCode = 500
res.end(res.sentry + "\n")
})
app.listen(PORT, () => console.log(`APP RUNNING ON PORT ${PORT}`))
The controllers on the server make database requests, etc. so they are async functions - that's why I use the wrap function to catch promise rejections and pass them to the error handler. If I unwrap the controller then Sentry works fine, but then the server never sends the error to the client.
I expect I'm probably going about this wrong as it should be pretty simple to do, but no matter what I do I cannot get Sentry + async controllers + custom error handler to work. Any help would be appreciated.
(This may be an Express issue, if so let me know and I'll take it over there)
Thanks
For some reason Sentry's 'Filter out localhost' option (which was turned off but somehow got toggled on) doesn't actually filter out all local errors. When I removed the custom error handler and wrap function, the errors managed to get past the localhost filter. After I turned it back off all the errors came through on Sentry fine.
While coding my app, I sometimes had a little mishap when typing my URLs in the browser, and thus sometimes got the error message:
Cannot GET /some/route
Which was true, since the route may was not defined.
But since this app is planned to enter production, I kinda don't want to use this flat message as my "error page".
Looking into the Express 4 docs, they tell me to .use() a middleware with 4 arguments. I did that. But I'd still get this issue...
Turns out that this message comes from the finalhandler module and my bet is, that this middleware comes before my error-catching, 4-argument middleware.
Here is a basic express app that I threw together while trying to find a solution:
var app = require("express")();
app.use("/yo", function(req, res, next){
res.send("Yo!");
});
app.use(function(error, req, res, next){
res.send("An error: "+error);
console.log(error);
});
app.listen(10000);
Accessing /yo works. But, / or /derp yields the Cannot GET message instead of my little middleware.
So, how is this done correctly, now?
The error middleware is only for actual errors, such as a middleware or route handler throwing an exception or passing an error to next().
If you want to provide a route handler for requests that do not match any existing routes, then just add a middleware after all of your app's routes/middleware like:
var app = require("express")();
app.use("/yo", function(req, res, next){
res.send("Yo!");
});
app.use(function(req, res, next) {
res.send('Could not route your request!');
});
app.use(function(error, req, res, next){
res.send("An error: "+error);
console.log(error);
});
app.listen(10000);
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 ...
In the following node.js server code, since "ABCDE" is not a defined a variable, error is thrown when '/upload' is requested. What confused me is that, the error stack trace printed on the console at server side is sent back to the client, which is unsafe.
How can I prevent that, other than catch that error?
var express = require('express');
var app = express();
app.use(function (err, req, res, next) {
res.send(500, 'Something broke!');
});
app.post('/upload', function (req, res) {
console.log(ABCDE);
});
app.listen(3000);
You already have the answer in your question. You need error-handling middleware (app.use a function with an arity of 4) to handle the error.
You just need to add the error-handling middleware after the router. Your example puts the the error handler above the the router.
Either move the app.use(function (err, req, res, next)) to the bottom of your code or insert
app.use(app.router);
above the error handler.
Refer to the documentation for more information about error handlers.
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