nodeJs express petitions from ajax don't show error's page - node.js

I have a configuration to serve a website with an error handler. It works fine with app.post requests, but it does not work with ajax petitions.
Here is my error handling middleware with an example of bad path error that works correctly:
//Bad path error
app.get('*', function(req, res, next){
next('404'); //This works fine
});
//Errors control Middleware
app.use(function (err, req, res, next) {
if (typeof (err) === 'string') {
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', err), {lang});
} else {
log.error(err);
var lang = require(path.join(__dirname, '_public', 'errors', 'lang', process.env.LANG + '.js'));
return res.render(path.join(__dirname, '_public', 'errors', '500'), {lang});
}
});
when I navigate to a wrong url (some url that isn't defined on my server) it goes correctly to the defined error page (404, that is an .hbs). The problem is that this method doesn't seem to work with ajax petition like the next one:
jQuery.ajax({
type: 'POST', //I have the same problem with 'GET'
url: '/componentName/methodName',
data: {
accessToken: localStorage.token
},
}).then(function success(data) {
//TODO
}, function error(err) {
console.log(err);
})
This piece of code is for server side:
app.post("/componentName/methodName", function (req, res, next) {
var token = req.body.accessToken;
var decodedToken = jwt.decode(token);
var user_id = decodedToken.payload.user_id;
model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}, function () {
next('500'); //This broke the page and doesn't show error page
});
});
What could be the problem for not showing the error from ajax? Is my syntax correct?
new: The problem must be on the ajax success, because if I change the 'post' by 'get' in ajax and the app.get route I still having the same problem, but if I call the method directly from URL (not ajax) it works. Any idea?
new2: If I put this code on success:
jQuery('body').html(data);
it shows the error page after a few seconds. I need to do this automatically (and without those fews seconds) when any error is trhowing from the server, because I don't know if it will be ok or if is an error and the lag seconds is a problem too. Probaly anything on the server could be wrong? and it inject the error page inside the older page, so isn't a solution

model.getTasks(user_id).then(function (modelData) {
res.send(modelData); //This works fine
}).catch(function () {
next('500');
})
Use catch block

Related

How to do AJAX POST call in express.js file in handlebar?

I am trying to send/update data to mongoDB database via AAJAX call but the command is not reaching theere. I have tried debugging using alert in between the code but the command is not reaching there. Means AJAX call doesn't get executed.
Below is my AJAX POST request code:
var text = "Done";
var data = {
selectedValue: text
}
$ajax({
method: 'POST',
url: '/update-sources',
dataType: 'text/json',
data: data,
success: function(data){
console.log(data);
alert("Working!!")
}
});
And Below is the /update-sources route code:
router.post('/update-sources', function(req, res, next) {
console.log("/Update-Sources")
User.findOneAndUpdate({email: req.user.email}, {$set:{status:data.selectedValue}}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
else
{
res.render('taskswriter');
console.log(doc);
return "Great Working!";
}
});
});
What mistake I am doing?
Would be great if you shared browser's console output, but trying to execute attached client-side snippet, I got the following error:
VM219:7 Uncaught ReferenceError: $ajax is not defined
at <anonymous>:7:1
You've got a typo there - it should be $.ajax as you are accessing a function from within jQuery namespace (https://api.jquery.com/jQuery.ajax/)

Express redirect error: can't set headers after they are sent

When this code hits the redirect line it throws the 'Can't set headers after they are sent error' and doesn't redirect. I'm guilty of not fully understanding headers and how express works with them. This link about this error is confusing me a bit, probably because I don't have a basic enough understanding of what's going on. Also, I know this is a bit of a naive approach to authenticating, but I'm just trying to get basic things to work.
app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
console.log("wrong pw")
}
})
UPDATE : thank you to #Brendan Ashworth I missed an obvious else, which I've added now and no longer get the error.
However this line doesn't change the contents of my page
res.sendfile('./public/admin/views/tunes.html')
It worked before I wrapped it with the auth check
var auth = require('../config/auth')
module.exports = function(app) {
/*
* CONTENT API
*/
//...
/*
* Admin Routes
*/
app.get('/admin/login', function(req, res) {
res.sendfile('./public/admin/views/login.html')
})
app.post('/api/login', function(req, res) {
if (req.body.password === auth.password) {
auth.date = new Date()
res.redirect('/admin')
} else {
res.json({message: 'Wrong password!'})
}
})
app.get('/admin', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/tunes.html')
console.log("test") //
} else { //added else
res.redirect('/admin/login')
}
})
app.get('/admin/:url', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/' + req.params.url + '.html')
} else { //added else
res.redirect('/admin/login')
}
})
// frontend routes
// route to handle all angular requests
app.get('*', function(req, res) {
res.sendfile('./public/views/index.html')
})
}
FINAL UPDATE!! The last thing I needed was to handle the redirect client side after sending the file. Simple authentication works perfectly now!
$http.post('/api/login', $scope.auth).success(function() {
window.location.href = '/admin'
})
An explanation of the error Can't set headers after they are sent error:
All HTTP responses follow this basic structure:
.. Response Line ..
.. Headers ..
.. Body ..
If you want to redirect a user, first the Response Line will be sent with a redirect code (lets say 300), then the Headers will be sent with a Location: xxx header.
Then, we can finally send a body (not in the case of a redirect, but in general). However - in the case with your code - you are sending a Body response then trying to redirect the user. Since the headers (and response line) have both already been sent (because you sent the body), it can't send more headers after the body.
An example of this in your code would be:
app.get('/admin', function(req, res) {
if (auth.date) {
res.sendfile('./public/admin/views/tunes.html')
}
res.redirect('/admin/login')
})
If I'm assuming right, you actually want to return after the res.sendfile() call. If auth.date is truthy, then you'll be sending a file (i.e. body response) and then giving a redirect code - that doesn't work.
after redirect just call res.stop();

expressjs - using middleware function

I would like to know how I can move the following code into a separate function (in the same file) and call upon it when either I call the POST or PUT routes to add or update documents.
I'm using https://www.npmjs.org/package/express-validator
The following is currently in my POST route but when I'm updating a record the title will still need to be validated.
app.post('/docs', auth, function (req, res) {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if(errors){
res.json(400, { errors: errors });
return;
}
//go ahead and save the document
});
I've tried making my own function but I'm not sure where to put the var errors = req.validationErrors(); or whether it's bad practice to return 400 errors from a separate function.
Any help/code much appreciated.
The body of the middleware function is almost identical to the code you are using right now, only with two notable differences:
The function ensures that the req.method is either POST or PUT.
The next() function is called when validation passes. This will trigger the next middleware function in the chain, or the route handler.
app.use('/docs', function(req, res, next) {
if (req.method == 'POST' || req.method == 'PUT') {
req.checkBody('title', 'Title is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.json(400, { errors: errors });
return;
}
}
next();
});
app.post('/docs', auth, function (req, res) {
// go ahead and save the document
});

Response headers of previous request affecting current request

The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.

Handle jade errors in res.render()

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

Resources