Here is a question I have concerning a Node.js app of mine (working with Express) on Heroku.
I want to handle unknown URL. More precisely:
If I look at this URL in a browser: https://myapp.herokuapp.com/ I see what I expect.
If I look a this one: https://myapp.herokuapp.com/xxyyyzzWhatever I also see what I expect. That is:
Cannot GET /xxyyyzzWhatever
Instead of displaying: Cannot GET /xxyyyzzWhatever
I want to do other things. At this point I have referred to these documents:
http://expressjs.com/en/guide/error-handling.html and http://thecodebarbarian.com/80-20-guide-to-express-error-handling; and I can get quite a bit of control on what is displayed.
I have implemented this kind of code:
app.get('*', function(req, res, next) {
setImmediate(() => { next(new Error('woops')); });
});
app.use(function(error, req, res, next) {
const xPt = req.path.substring(1,req.path.length);
res.json({ message: error.message reachPoint: xPt});
});
But what I really want is to have something like:
if (xPt.substring(0,1) == "A") {display("You are lucky");}
else if (xPt.substring(0,1) == "Z") {display("You are very unlucky");}
else if ((xPt.substring(0,1) >= "B")&&(xPt.substring(0,1) <= "R"))
{goto("https://stackoverflow.com/")}
else {goto("http://www.google.com/")}
Any tip on the way to go?
you can use following:
app.use(function (req, res, next) {
res.status(404).send("Sorry can't find that!")
});
https://expressjs.com/en/starter/faq.html
Related
How should I handle routes in Express when users enter random URL's that include multiple forward slashes?
For URL's that don't match actual directories I can do this but it is really horrible. When i go to mysite.com/randomNoise/anythingHere/blahblah the following will render my 404...
app.get('/:anything1/:anything2/:anything3', (req, res) => {
res.render("../theme/404")
})
Like I said, it's not very nice and will not work when even more sub directories are added, I end up with the standard cannot GET / etc Example:
mysite.com/something/somethingelse/whatever/extraGarbage
Is there a way to stop Express sending "Cannot GET /" messages or a way to handle when that happens?
You'll be wanting a custom express error handler.
Put code like this after all other app.use() express calls. It takes advantage of the fact that calling next() with a parameter is how you tell express to return an error, rather than a result, to a user.
const createError = require('http-errors')
...
app.use(function (req, res, next) {
next(createError(404, req.path + ' not found.'))
})
app.use(function (err, req, res, next) {
/* display a message */
res.locals.message = err.message
/* suppress the traceback when not in development */
res.locals.error =
req.app.get('env') === 'development' ? err : {}
if (req.accepts('html')) {
res
.status(err.status || 500)
.render('error')
} else if (req.accepts('json')) {
res
.status(err.status)
.json({
status: err.status,
name: err.name,
message: err.message
})
} else {
res
.status(err.status)
.send(`${err.status}: ${err.message}\r\n`)
}
})
and create yourself an error.pug template, or use whatever template engine you chose.
You can fiddle around with this code to give the exact error presentation you want.
i am making multiple streamdata in Nodejs using express.
this is how i make a url:
app.get('/temp/1', function(req, res){
res.send('hello, i am not modified')
})
my question is: is it possible to modify the response of that url?
i tried like this:
app.get(/modify/1, function(req, res){
app.get('/temp/1', function(req, res){
res.send('hello, i am modified')
})
res.send('done');
}
So i would think that the response is changed, but nothing happens actually.
is there a way to achieve it?
Here's an example using express-modify-response:
const modifyResponse = require('express-modify-response');
...
let modify = modifyResponse(
function(req, res) { return true }, // always modify the response
function(req, res, body) { return 'hello, i am modified' } // the new response
);
app.get('/temp/1', modify, function(req, res){
res.send('hello, i am not modified')
})
EDIT: second attempt. You have an endpoint /temp/1 that sends a particular response, and you want an endpoint /modify/1 that will take that response and modify it.
This calls for some abstraction:
function someFunction(id) {
return 'hello, i am not modified';
}
app.get('/temp/1', function(req, res) {
res.send(someFunction(1));
});
app.get('/modify/1', function(req, res) {
let value = someFunction(1);
// Remove the word `not`.
value = value.replace(/not /, '');
res.send(value);
});
So both handlers use the same function, that provides the actual output, but /modify/1 modifies the output before returning it to the client.
What is the advantage of catching errors and responding from within a middleware function like in example A.
Example A
function(req, res, next)
{
if (err) {
err.message = 'Not Found';
res.status(404).json(err);
return;
}
}
Versus passing the error to the error handling middleware like in example B.
Example B
function(req, res, next)
{
if (err) {
var err = new Error('Not Found');
err.status = 404;
next(err);
}
}
error handling middleware:
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.json({message: "error"});
});
Imagine you are piping the request through several functions to process it, and the error in one of the first functions is superficial. You can still process the request, but you need to send a warning or handle the error afterwards. Perhaps you want to gather all of the errors in the pipeline report them back. Example B allows this.
But imagine the error is critical, and you know you won't be able to proceed. In that case you, can't call next, because the next function won't know how to deal with a fundamentally broken request. Here is where example A comes in handy.
I'm using MEAN stack from meanjs and have this routes:
// Teams Routes
app.route('/teams')
.get(teams.list)
.post(users.requiresLogin, teams.create);
app.route('/teams/:teamId')
.get(teams.read)
.put(users.requiresLogin, teams.update)
.delete(users.requiresLogin, teams.delete);
app.route('/teams/:teamId/participants')
.get(teams.teamParticipants);
// Finish by binding the Team middleware
app.param('teamId', teams.teamByID);
The issue here is, whenever I'm accessing a resource with this path:
[GET]
http://localhost:3000/teams/547dd53b964b3514294d2dfe/participants
it always return a 404 status. When the request reaches the server, it's accessing
teams.teamByID
from param but wasn't been able to proceed to:
teams.teamParticipants
What I wanna know if there's something I'm doing wrong when it comes to defining my routes, and if there's any better way of defining routes.
Thank you in advance.
EDITS
#mscdex
Here's my teamByID
exports.teamByID = function(req, res, next, id) {
Team.findById(id).exec(function(err, team) {
if (err) return next(err);
if (! team) return next(new Error('Failed to load Team ' + id));
req.team = team ;
next();
});
};
I found the problem here. I dig into express' code and checked how it handle its routes.
Express handles the routes callbacks based on the number of arguments the function has.
If the function for the route has four(4), like the one I have:
exports.teamParticipants = function(req, res, next, id) {
Participant.find({team: id}, function(err, participants){
if (err) return next(err);
if (! participants) return next(new Error('Failed to load Participants from Team ' + id));
res.jsonp(participants);
next();
});
};
It would use its 'handle_error' of its Layer class, passing four arguments: error, req, res, and next.
If the route has less than 4 arguments, it would use 'handle_request' method of it Layer class, passing 3 main arguments: req, res, next. So correcting my 'teamParticipants' method, I should have this kind of implementation for it to work:
exports.teamParticipants = function(req, res) {
Participant.find({team: req.team._id}, function(err, participants){
if (err){
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(participants);
}
});
};
So far, the issue here was Express handles param and route differently. I thought that param and route passed the same arguments.
param handler has this signature: param(req, res, callback, value, key)
which is different from routes
route's handler signatures:
route(req, res, next)
route(err, req, res, next)
I've been using this npm module, expresspath. It separates your controllers/middlewares. :)
I have a working node.js / express based server and am using jade for templating. Usually there is no problem but a couple of times every day I get an error message when requsting any page. The error is 'failed to locate view'. I don't know why i get this error since it worked fine just minutes before.
The question however is how I can force a crash on this event, for example:
res.render('index.jade', {info: 'msg'}, function(error, ok) {
if (error)
throw new Error('');
// Proceed with response
};
How would I do this? And how would I proceed with the response?
thank you.
You can add an error handling middleware.
app.use(function handleJadeErrors(err, req, res, next) {
// identify the errors you care about
if (err.message === 'failed to locate view') {
// do something sensible, such as logging and then crashing
// or returning something more useful to the client
} else {
// just pass it on to other error middleware
next(err);
}
});
Try this:
app.use(function (req, res, next) {
fs.exists(__dirname + '/views/' + req.url.substring(1) + '.jade', function (exists) {
if(!exists) {
console.log(err);
return next();
}
res.render(req.url.substring(1), { title: "No Controller", user: req.session.user });
}
});