so I am running into what I think is a binding issue, caused by connect-mongo or expressjs Here is the code:
//Error
app.use(function(err, req, res, next) {
if (err instanceof noData) {
res.send(err, 404);
} else {
next(err);
}
});
My custom error handler
function noData(err){
this.code = 0;
this.msg = err;
console.log(Error);
Error.call(this, {code:0, msg:err});
Error.captureStackTrace(this, arguments.callee);
};
noData.prototype.__proto__ = Error.prototype;
Throwing error here:
err = true;
//if(err) throw new noData('No Password');
//Get user from database
db.collection('users').find({}, {limit:1}).toArray(function(err, result) {
if(err) throw new noData('No Data');
});
The first error throws correctly, but the second one but the second one throws a general nodejs error.
throw e; // process.nextTick error, or 'error' event on first tick
What am i doing wrong here? Is connect-mongo causing it to lose binding somehow?
Any thoughts are greatly appreciated.
The problem doesn't lie in express or connect-mongo, the callback is in a different scope. To resolve this simply add a (this) to the end of the call.
//Get user from database
db.collection('users').find({}, {limit:1}).toArray(function(err, result) {
if(err) throw new noData('No Data');
}(this));
Now node knows about my custom error. (hallelujah)
This essentially an iife which allows me to pass in a param.
Related
I have an quite simple application the idea is that someone has unique code which value are stored in one mongo collection in other we are keeping some data which we need to return if the key was found in first collection.
As probably you have noticed I'm using NodeJS with MongoDB and Mongoose, Express.
I have a problem with method bellow:
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.send(err);
}else{
SampleData.findOne({}, function(err, sample_data){
if(err)
res.send(err);
res.json(sample_data);
});
}
});
};
The problem is that it will always return the data beause it's not throwing an error but empty array - so is there any other good and proper way as it should be don to throw 404 error without statement such as if(length<0) res.status(404).send('Error message).
I simply want to minimalize amount of if statements.
Maybe there is some other way to write implementation od error handling for mongoose which in general instead returning empty array will give us error code with message?
It's not exactly clear what you're asking, but if you want to make an error condition out of something that is not normally an error, then an if statement (or some other test like that) is required to test for that specific condition.
You could make your own function for querying that turns an empty response into an error and you could "hide" the if condition in that function if you want, but it's still an if condition that tests for your specific condition.
So, to return a 404 if the array is empty, you would just add an if statement (as you already appear to know):
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
SampleData.findOne({}, function(err, sample_data){
if(err) {
res.status(500).send(err);
} else {
if (sample_data.length) {
res.json(sample_data);
} else {
res.status(404).send("no data");
}
}
});
}
});
};
FYI, you also need to make sure you are properly setting a status code when there's an error and that you are never sending multiple responses to the same request (even when there's an error). I've also fixed several cases of those issues in your code.
This could likely be written cleaner and responses consolidated by using the promise interface to your database and send an error in one .catch().
For example, you could simplify your code by creating a utility function for .findOne() that detects and sends an error response automatically:
function findOne(res, db, q, cb) {
db.findOne(q, function(err, data) {
if (err) {
res.status(500).send(err);
cb(err);
} else if (!q.length) {
res.status(404).send("no data");
cb(new Error("no data"));
} else {
cb(null, data);
}
});
}
Then, your function could be simplified to this:
exports.getCompanyByKey = function(req, res) {
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
findOne(res, SampleData, {}, function(err, sample_data) {
// any error response has already been sent
if (!err) {
res.json(sample_data);
}
});
}
});
};
Again, this would be better to use your Db's promise interface.
I have already added the reference,but it still tells me the error like picture 2.
message of the error
I'm the new to learn node.js and don't understand the reason of it.
You are not handling errors !
You have error in while reading a file.
var on404 = function (req, res, next) {
fs.readFile("server/404.html", "utf-8", function (err, file) {
if(err) return next(err); // If error than do not proceed
//
// YOU OTHER CODE GOES HERE ....
//
// ...
});
}
FYI: No need of else as if it goes in if, then function execution ends with return statement.
I am trying to send an error if a condition is true using the Mongoose function findById. The problem is that Mongoose appears to be setting the res Express object and is then throwing an error when I try to set the headers myself. Here is the code:
console.log(res.headersSent); // false
Trade.findById(req.body.trade, function (err, trade) {
if (err) throw err;
// Ensure user is not making an offer on their own item
Item.findById(trade.listing, function (err, item) {
if (err) throw err;
if (req.decodedId == item.user) {
console.log(res.headersSent); // true (?)
return res.status(403).send({
success: false,
message: 'You cannot make an offer on your own item'
})
} else {
return;
}
})
And here is the stack trace for the error:
false // res.headersSent() before calling Trade.findById()
POST /api/v2/offer 200 148.799 ms - 162
true // res.headersSent() after calling Item.findById() and checking error condition
_http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:154:12)
at ServerResponse.json (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:240:15)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:142:21)
at /Users/Matt/Dropbox/work/TradeRate/prototype/server/controllers/offers.js:48:40 // LINE THAT CONTAINS return res.status(403).send ...
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/lib/query.js:1169:16
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/node_modules/kareem/index.js:103:16
at process._tickCallback (node.js:355:11)
18 Jul 15:26:39 - [nodemon] app crashed - waiting for file changes before starting...
What could be causing this error? Is there aspect of the Mongoose API that sets the response headers that I'm missing?
EDIT: I added my full (updated) exported route handler in case that has some context that would make the problem more clear.
// POST /api/offer
exports.createOffer = function (req, res, next) {
console.log(res.headersSent);
Trade.findById(req.body.trade, function (err, trade) {
if (err) {
next(err);
return;
} // not good to throw from async events, let express' error handling middleware take care of it
// Ensure user is not making an offer on their own item
Item.findById(trade.listing, function (err, item) {
if (err) {
next(err);
return;
}
if (req.decodedId == item.user) {
console.log(res.headersSent); // true (?)
res.status(403).send({
success: false,
message: 'You cannot make an offer on your own item'
});
}
// all done with async stuff, pass the request long
next();
});
// If trade is expired, reject the offer
if (trade.expiresOn < Date.now()) {
res.status(403).send({
success: false,
message: 'This trade has expired and cannot accept new offers'
});
}
// Create new offer and add data
var newOffer = new Offer();
newOffer.items = req.body.items;
newOffer.trade = req.body.trade;
newOffer.save(function (err, offer) {
if (err) throw err;
});
// Add offer to items in offer
for (var i = 0; i < req.body.items.length; i++) {
Item.findById(req.body.items[i], function (err, item) {
if (err) throw err;
item.offers.push(newOffer._id);
item.save(function (err, item) {
if (err) throw err;
});
});
}
// Add offer to trade
trade.offers.push(newOffer._id);
trade.save(function (err, trade) {
if (err) throw err;
});
return res.send(newOffer);
});
};
Completely new answer, disregard my old one it was all wrong (it's been a while since I've used express).
Anyway the problem is you're calling async functions which return immediately so at the bottom there when you're calling return res.send(newOffer);, you're doing it before any of those callbacks return. So you returned before you
Check if the user is trying to create an offer on their own item
Add the new offer id to the items
Save any of those changes
Another problem is your loop there will likely fail horribly. There's no guarantee that you'll be pushing those items in order because findById and save as async, they return instantly and may be executed in any order. Plus there's no reason at all to save after every push. You need to either wait for each findById to return before continuing the loop (so you can't use a basic for loop, most likely a callback) or more correctly, just use a mongoose update query to do this all at once (you don't need to load an item to push an offer to it, just use $push)
The best way to handle all of this in express is with middleware. So change your code to this (I've added a dependency on http-errors to make error handling easier.
I'm assuming you're using the most recent version of express:
The Offer Route
var httpError = require('http-error') // needed for ezpz http errors
var express = require('express'); // needed for express.Router()
// middleware that loads the trade
function loadTrade(req, res, next) {
Trade.findById(req.body.trade, function (err, trade) {
req.trade = trade;
next(err, trade);
})
}
// middlware that checks expiration
function checkExpired(req, res, next) {
if(req.trade.expiresOn < Date.now())
next(httpError(403, 'This trade has expired and cannot accept new offers'));
else next();
}
// middleware makes sure the user isn't making an offer on their own item
function checkIsOwner(req, res, next) {
Trade.findById(req.trade.listing)
.select('user')
.exec(function(err, listing) {
if (err) next(err)
else if (listing.user == req.decodedId) next(httpError(403, 'You can not make an offer on your own item'))
else next();
})
}
// now we can create an offer
function createOffer(req, res, next) {
// req.trade was loaded and validated by our middleware
// if next(err) was called at any point this function wouldn't be called
var trade = req.trade;
Offer.create({trade: trade._id, items: req.body.items}, function (err, offer) {
if (err) {
next(err); // we only call next to trigger the error handler
return;
}
// now push the new offer id to all the items
Item.update({$in: req.body.items}, {$push: offer._id}, function (err, offer) {
if (err) next(err)
else res.json(newOffer);
})
});
}
exports.createOffer = express.Router()
.post(loadTrade)
.post(checkExpired)
.post(checkIsOwner)
.post(createOffer);
For handling errors I'd add this after you've setup all the routes (where you have your app.post('/api/v2/offer', ....) stuff:
app.use('/api/v2/*', function(err, req, res, next) {
res.status(err.status || 500).json({ success: false, message: err.message });
});
Now whenever you call next(err), this error handler will be called and send a status code and error message.
I am working with MongoDB using Mongoose. Most of the opeartion works with callback. An error may occur while saving/updating/finding a document. Though we can always check if there an error in callback function (as shown in below code) but I want to know while developing how can we generate error and test these blocks?
Tank.findById(id, function (err, tank) {
if (err) return handleError(err);
tank.size = 'large';
tank.save(function (err) {
if (err) return handleError(err);
res.send(tank);
});
});
Are you familiar with the Error class? Emiting errors with the EventEmitter? Throwing errors with throw?
This link is a fairly extensive overview on how to deal with errors in node.
Assuming your using express, in the case of the example you provided, I would usually create an instance of the Error class doing something like:
exports.findTankById = function(req, res, next) {
var id = req.params.id;
Tank.findById(id, function (err, tank) {
if (err) {
var e = new Error("Failed to find tank");
e.data = err;
// attach other useful data to error class instance
return next(e);
}
return res.status(200).json({ data: tank });
})
});
Then in another part of the application, have a middleware function that catches errors passed by your routes via next(). That function could log the error or doing something more creative. Note that when using new Error(...) you can access the stack using the stack attribute of the Error class (e.g. err.stack). After processing the error, the error handler function would send back an appropriate response.
A simple error handler function could look something like:
app.use(function (err, req, res, next) {
if(err.data) {
// caught operational errors
/* do something to log or process error */
var response = {
type : 'error',
description : err.message // this would use "Failed to get tank" for above example
};
res.status(500).json(response);
} else {
// unexpected errors
var domainThrown = err.domain_thrown || err.domainThrown;
var msg = 'domainThrown: ' + domainThrown + '\n' + err.stack;
console.error('%s %s\n%s', req.method, req.url, msg);
res.set('Connection', 'close');
res.setHeader('content-type', 'text/plain');
res.status(503).send(msg + '\n');
}
});
If you like this approach, I usually define more specific error objects of my own that more or less extend the Error class. Using functions to create the more specific error types limits the need to write out the
var e = new Error("Failed to find tank");
e.data = err;
/* attach other useful data to error class instance */
part every time. Using more specific error objects also forces consistency on how the errors are being formatted. Hope that is helpful,
Craig
With synchronous errors, you can nest error scopes like this:
try {
try {
throw Error('e')
} catch(e) {
if(e.message !== 'f')
throw e
}
} catch(e) {
handleError(e)
}
This is how I would expect it to work, but it doesn't (seems an error inside a domain error handler is thrown up to the top, skipping any domains in between):
var domain = require('domain');
var dA = domain.create();
dA.on('error', function(err) {
console.log("dA: "+ err); // never happens
});
dA.run(function() {
var dB = domain.create();
dB.on('error', function(err) {
throw err
});
dB.run(function() {
setTimeout(function() {
console.log('dB')
throw 'moo'
},0)
});
});
Is there a way to do this right?
Bubbling doesn't work in domains through rethrowing. If you want to pass an error off to another domain you know can handle an error, you can re-emit the error event on that domain directly:
var domain = require('domain');
var dA = domain.create();
dA.on('error', function(err) {
console.log("dA: "+ err); // never happens
});
dA.run(function() {
var dB = domain.create();
dB.on('error', function(err) {
dA.emit('error', err);
});
dB.run(function() {
setTimeout(function() {
console.log('dB')
throw 'moo'
},0)
});
});
To expand a little, the problem with throwing from a domain's error handler is that it propagates directly to the top level, and even more confusingly, if the throw is the result of an error in the error handlier, is that the stacktrace that gets printed out is from your original error, not the new error in the handler. Theoretically it would be possible to bubble exceptions up the stack, but that's not how domains were designed.
The "nested" domains will work properly if the handler of an outer domain throws while an inner domain is active, but what it does in that case is give the error to the outer domain's error handler and then exits both the outer and the nested domain. This mimics how a catch unwinds the stack in the try/catch case, but it can be a little confusing.