Catching error when URL param does not match regex in Express - node.js

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")
});

Related

Why might Express error handling middleware not receive req.session?

Environment: node.js, Express, express-session package.
Background: I was testing an Express error handler and I got an error that I wasn't expecting.
The following simplified route throws an error.
TypeError: Cannot read property 'userValues' of undefined
exports.errorHandler = wrapAsync(async function(error, req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
However when I remove the error parameter it works without an error as I had anticipated.
exports.errorHandler = wrapAsync(async function(req, res, next) {
let loggedIn = req.session.userValues ? true : false;
res.render('error', { loggedIn });
});
Why might this be?
The basic pattern in the second example works in several dozen routes that don't include the error parameter.
You could use something like this. And it will only get executed whenever there is an ERROR 500 unless you passed the ERROR 404 to this one using next() function, if you handled all the errors correctly you should be able to make an ERROR 500 and this should be able to catch that ERROR.
const errorHandler = require("./your-file");
...
... every other route even error 404 handler
...
app.use(errorHandler);
What do I mean by using next() for ERROR 404
If you have used express-generator then you should already have this piece of code:
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
The end file should looks something like this now if you use this approach:
...
... all previous routes
...
// catch 404 and forward to error handle
app.use((req, res, next) => {
next('not found');
});
// handle all other error
app.use(errorHandler);
Hope this helps

How to handle errors in Express 4 from within the Node.Js app?

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.

404 usage with router and different routing files

I use in my app different routes. All this routes have a topic file. Like:
index.js -> for basic routing in the / level. Like /welcome or
/dashboard
cases.js -> for using edit forms Like /cases/case_create
tcafe.js -> for testing routes like /tcafe/startit
users.js -> for user operation like /users/login or /users/register
In my app I use:
// Routes
app.use('/', require('./routes/index.js'));
app.use('/users', require('./routes/users.js'));
app.use('/cases', require('./routes/cases.js'));
app.use('/tcafe', require('./routes/tcafe.js'));
Ok, now I want to add a 404 handler so I can avoid "Cannot GET /dashboard2"
I added now to the end of all route files:
router.use((req, res, next) => {
next({
status: 404,
message: 'Not Found',
});
});
router.use((err, req, res, next) => {
if (err.status === 404) {
return res.status(400).render('404');
}
if (err.status === 500) {
return res.status(500).render('500');
}
next();
});
now I get a 404 if using "/dashboard2" but all other routes also get a 404
like "/users/login" or "/cases/create_case"
Moving the code to the main js file (server.js) also do not work.
Anybody has a Idea how to protect the whole app with 404 for all routes?
As stated in the official Express FAQ:
All you need to do is add a middleware function at the very bottom of the stack (below all other functions) to handle a 404 response
So in your case, you need to put your 404-handler after all of the router.use() calls.

Nodejs handle unsupported URLs and request types

I would like to add to my web service an option to handle unsupported URLs, I should mention that I'm using Express.
In order to handle bad URLs, (code 404), I tried using
app.use(app.router);
But apparently it's deprecated, what other solutions can I use?
I saw this suggested solution but I would like to hear about other alternatives first.
In addition, my web service support a few HTTP request types, such as GET and POST, how do I properly respond to request types that I do not support? such as DELETE.
The behavior I would like to have is that in case of 404 error, I will return an appropriate response message that's all. Same in case of unsupported requests.
For example:
response.status(404).json({success: false,msg: 'Invalid URL'});
A 404 handler for all unhandled requests in Express would typically look like this:
app.use(function(req, res, next) {
res.status(404).sendFile(localPathToYour404Page);
});
You just make this the last route that you register and it will get called if no other routes have handled the request.
This will also catch methods that you don't support such as DELETE. If you want to customize the response based on what was requested, then you can just put whatever detection and customization code you want inside that above handler.
For example, if you wanted to detect a DELETE request, you could do this:
app.use(function(req, res, next) {
if (req.method === "DELETE") {
res.status(404).sendFile(localPathToYour404DeletePage);
} else {
res.status(404).sendFile(localPathToYour404Page);
}
});
Or, if your response is JSON:
app.use(function(req, res, next) {
let obj = {success: false};
if (req.method === "DELETE") {
obj.msg = "DELETE method not supported";
} else {
obj.msg = "Invalid URL";
}
res.status(404).json(obj);
});
Some references:
Express FAQ: How do I handle 404 responses?
Express Custom Error Pages
And, while you're at it, you should probably put in an Express error handler too:
// note that this has four arguments compared to regular middleware that
// has three arguments
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
This allows you to handle the case where any of your middleware encountered an error and called next(err).

koa-static going to next middleware

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)

Resources