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)
Related
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.
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 some code below:
app.get('/tx/:txhash([a-zA-Z0-9]{20,50})', (req, res) => {
//do some work
}
My issue is if the parameter does not match the regex pattern, I get
Cannot GET /tx/8241fesf
But I'm not sure how to have a custom error or redirect. I tried to read the res object but it seems it's skipped altogether and haven't found the answer searching on SO.
You can handle 404 withing express handlers.
In your main express file(may be index.js or app.js) just put following after your routing middleware.
app.use("/", your_router);
// catch 404 and forward to error handler
app.use((request, response, next) => {
// Access response variable and handle it
// response.status(404).send("Your page is not found"))
// or
// res.render("home")
});
Until now I've defined my get and post handlers with just (req, res) as arguments, with the assumption being that I put these handlers last in the chain of middleware, and make sure that I handle any responses and error handling properly within these handlers... hence it doesn't matter that I don't make any reference to next.
Is this a valid and sensible approach, or is it good practice always to call next() even if (at present) there is nothing coming afterwards? For example, perhaps in the future you might want to do some handling after these routes... or maybe there's a reason I haven't yet come across why it's good practice to always call next().
For example, there is the following simple example in the express routing guide:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
Of course, I appreciate that this is a very simple example to illustrate that handlers can be chained, and is not intended to provide a complete framework for a get handler, but would it be better to define and use next even in the second handler, as follows?
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res, next) {
res.send('Hello from B!')
next()
})
Or is it actually common practice to assume that a handler function that sends a response back to the client should not call next()... i.e. the assumption should be that the chain will end at the handler that actually sends the response?
Or is there no established practice on this point?
I'm even wondering whether it might be common not to send any response in the get handler but to defer that to a dedicated response handler coming after... by which I mean an OK response handler rather than an error response handler (for which it seems to be common practice to defined a final error handler and call next(err)). So, in a non-error situation, you would call next() and in the following middleware you would do your res.status(200).send(req.mydata) where req.mydata is added in your get handler.
No. You should only call next() if you want something else to handle the request. Usually it's like saying that your route may match that request but you want to act like it didn't. For example you may have two handlers for the same route:
app.get('/test', (req, res, next) => {
if (something) {
return next();
}
// handle request one way (1)
});
app.get('/test', (req, res) => {
// handle request other way (2)
});
Always the first matching handler is called, so for the GET /test request the first handler will be called, but it can choose to pass the control to the second handler, as if the first didn't match the request.
Note that if the second handler doesn't intend to pass the request to the next handler, it doesn't even have next in its arguments.
If there was no second handler, then the standard 404 handler would be used if the first one called next().
If you pass an argument to next() then an error handling middleware will be called.
My rule of thumb is to handle the response in the handler if you're going to give a 20x (Success) response code, and in centralized error handling if not. That looks something like this in practice:
// ./routes/things.js
const express = require('express');
const Thing = require('../models/thing');
const Router = express.Router();
// note, the handlers might get pulled out into a controllers file, if they're getting more complex.
router.param('thingId', (req, res, next, id) => {
Thing.findById(id, (e, thing) => {
if (e) return next(e);
// let's say we have defined a NotFoundError that has 'statusCode' property which equals 404
if (!bot) return next(new NotFoundError(`Thing ${id} not found`));
req.thing = thing;
return next();
});
});
router.get('/', (req, res, next) => {
// possibly pull in some sort, limit, and filter stuff
Thing.find({}, (e, things) => {
if (e) return next(e);
res.send(things);
});
});
router.route('/:thingId')
.get((req, res) => {
// if you get here, you've already got a thing from the param fn
return res.send(req.thing);
})
.put((req, res, next) => {
const { name, description } = req.body; // pull whitelist of changes from body
let thing = req.thing;
thing = Object.assign(thing, { name, description }); // copy new stuff into the old thing
thing.save((e) => {
if (e) return next(e);
return res.send(thing); // return updated thing
});
});
Keeping each logical chunk in its own file can reduce repetition
// ./routes/index.js then mounts the subrouters to the main router
const thingsRoute = require('./things');
const express = require('express');
const router = express.Router();
/* .... other routes **/
router.use('/things', thingsRoute);
Error handling is then centralized, and can be mounted either in its own file or right on the app:
// in ./index.js (main app entry point)
const express = require('express');
// this will require by default ./routes/index.js
const routes = require('./routes');
const app = express();
const log = require('./log');// I prefer debug.js to console.log, and ./log.js is my default config file for it
/* ... other app setup stuff */
app.use(routes);
// you can mount several of these, passing next(e) if you don't handle the error and want the next error handler to do so.
app.use((err, req, res, next) => {
// you can tune log verbosity, this is just an example
if (err.statusCode === 404) {
return res.status(404).send(err.message);
}
log.error(err.message);
log.verbose(err.stack); // don't do stack traces unless log levels are set to verbose
return res.status(500).send(err.message);
});
For every request that happens, I'd like to check if a parameter in the query string is set. If not, the application should send a certain message; otherwise, route as appropriate.
In app.js:
app.use(function(req,res,next){
if(req.query.key === undefined) {
res.send("Sorry!");
}
req.db = db;
next();
});
app.use('/', routes);
When '/' is requested without the param, Sorry! is displayed. However, my ExpressJS app crashes with this error:
Error: Can't set headers after they are sent.
I'm not entirely sure why this is happening. I've tried moving the check to the route itself in index.js, but I still get the same error.
That's because you're still continuing on with the execution and calling next(), which moves onto the next middleware or route in the stack.
Return early to stop it from moving onto the next middleware.
app.use(function(req,res,next){
if(req.query.key === undefined) {
//return out of the function here
return res.send("Sorry!");
}
req.db = db;
next();
});
app.use('/', routes);