So I am working on a social networking project, I added the ability to upload images and an API route that can return image objects from a DB so that I can display them. This was all working fine, then I took a few days break from it (new game came out) and now the API rout isn't working, sort of.
So here is the route:
router.get("/image/all/:id", function(req, res, next) {
console.log("Triggered: Images/all/ID");
Image.find({owner_id: req.params.id}).populate("comments").exec(function(err, images) {
if(err) {
req.flash('error', err.toString());
return next();
} else {
console.log("images API trigger");
res.json(images);
return next();
}
});
});
And here is the ajax call:
$.ajax({url: "/image/all/" + uid, success: function(data) {
for(let i = 0; i < data.length; i++){
let img = "<a class='aPic' href='#' data-toggle='modal' data-target='.view-image-modal' data-id='" + data[i]._id + "'> <img src='" + data[i].thumb + "' class='thumbnail pull-left'></a>";
$('#proBox').append(img);
imageData.push(data[i]);
}
$('.view-image-modal').on('show.bs.modal', function(e) {
var button = $(e.relatedTarget);
var id = button.data('id');
var image;
for(let i = 0; i < imageData.length; i++) {
console.log(imageData[i]._id + " | " + id);
let imId = imageData[i]._id.toString();
if(imId == id){
image = imageData[i];
console.log("Found: " + imageData[i].file);
}
}
let splt = image.file.split(".");
let ext = "." + splt[splt.length - 1];
$('.modal-content img').attr('src', "/images/uploads/" + id + ext);
$('.modal-title').html("Uploaded: " + moment(image.created).format("MM/DD/YYYY"));
$('#imgDesc').val(image.description);
$('#imgEdit-button').attr('data-id', id);
$('#imgDelete-button').attr('data-id', id);
//Update comments section
});
}});
So I get this url: http://127.0.0.1:8080/image/all/5989da1afdc58624a0b10fda
the console gives error 404, if I put this in the address bar i get "Cannot GET ..." however if I add or remove a letter from the id, it returns an array as it is supposed to.
I also tried this in an incognito window to make sure it wasn't a cached file issue or anything like that. Any ideas?
In the code above, when an error occurs, you call next(). If you do that and you do not have an error handler, then you will get a 404. In your error case, return res.json(err) instead of return next() and you might actually see why it is failing.
Alternatively you can add an error handler as your final middleware and call next(err).
Related
I understood why it was an error, I tried the suggestions like return or do function aysnc, but I could not solve it.
I have documents as long as find.lenght and 100 sub-documents. That's why I am use two loop. When first iteration is complete and second matching value is found, server is crashed. Without server it works.
I have documents called 0,1,2 and there are 100 crypto coins records from 0 to 100 in them. I added an image. For example, USDT is at [0,3], [1,43], [2,13] and I want to send all of three.
app.post('/', (req, res) => {
print(req, res)
})
function print(req, res) {
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("cryptoDb");
dbo.collection("coinTable").find({}).toArray(function (err, find) {
if (err) throw err;
for (i = 0; i < find.length; i++) {
let found = false;
for (j = 0; j < 100; j++) {
//console.log(i + " " + j)
let id = Capitalize(req.body.coinName);
if (find[i].result[j].name == id || find[i].result[j].symbol == id.toUpperCase()) {
// console.log(find[i].result[j]);
res.send(find[i].result[j]);
found = true;
}
}
if (!found) {
console.log("Not found")
}
}
db.close();
});
});
function Capitalize(s) {
return s[0].toUpperCase() + s.slice(1).toLowerCase();
}
}
Thank you so much !
This error comes from attempting to send more than one response to the same http request.
You have res.send(find[i].result[j]); inside a for loop and you do not stop the inner loop after sending a response.
Thus, this code is capable of attempting to send multiple responses to the same request which you cannot do.
It's unclear from the code exactly what the desired solution is. If you only want to send the first response, then you can close the db and return after you send a response which will terminate both for loops.
If you intend to send multiple pieces of data, then accumulate the data you want to send in an array and send all the data once after all the loops are done.
If you're trying to send an array of all matching results, you can do this:
app.post('/', (req, res) => {
print(req, res)
})
function print(req, res) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
res.sendStatus(500);
return;
}
var dbo = db.db("cryptoDb");
dbo.collection("coinTable").find({}).toArray(function(err, find) {
if (err) {
console.log(err);
res.sendStatus(500);
db.close();
return;
}
const id = Capitalize(req.body.coinName);
const idUpper = id.toUpperCase();
const results = [];
for (let findItem of find) {
for (let j = 0; j < 100; j++) {
if (findItem.result[j].name == id || findItem.result[j].symbol == idUpper) {
results.push(findItem.result[j]);
}
}
}
res.send(results);
if (!results.length) {
console.log("No results");
}
db.close();
});
});
function Capitalize(s) {
return s[0].toUpperCase() + s.slice(1).toLowerCase();
}
}
Other Notes:
I changed the if (err) { ... } handling on your database call to actually send an error response. All paths through your request handler need to send a response or cause a response to be sent.
The hard coded loop from 0 to 99 is a bit odd as you don't check if the .result array actually has that many entries in it. That could result in a run-time error if the data isn't exactly how you are expecting it.
You don't have any validation of the req.body data you are expecting. All data arriving from the user should be validated before assuming it is what you are expecting.
You have res.send inside of your for loop, im assuming you want it to quit once its done, so add break to your loop.
var senddata = [];
for (j = 0; j < 100; j++) { // <-- For loop, it will send multiple times
// console.log(i + " " + j)
let id = Capitalize(req.body.coinName);
if (find[i].result[j].name == id || find[i].result[j].symbol == id.toUpperCase()) {
// console.log(find[i].result[j]);
senddata[senddata.length] = find[i].result[j]); // Add to array
found = true;
}
if (!found) {
console.log("Not found")
}
}
res.send(JSON.stringify({senddata})); // Send unparsed data
Working with NodeJS for the first time, trying to build a public endpoint that an accept an XML file, convert it to JSON, save it to MongoDB, then send a 200 HTTP status code, if everything went well. But the Express route completes and sends a response long before the code writing to the database completes.
A slightly simplified version:
app.post('/ams', function (req, res) {
try {
if (Object.keys(req.body).length === 0) {
console.log("request body was empty");
// throw new EmptyBodyException(req.body);
message = message + "Request body was empty \n";
}
let body_keys = Object.keys(req.body);
let body_values = Object.values(req.body);
let xml_string = body_keys[0] + ":" + body_values[0];
let xml = xml_string.trim();
console.log("The trimmed XML:");
console.log(xml);
// convert XML to JSON
xml2js.parseString(xml, (err, json) => {
if(err) {
message = "xml2js.parseString failed, xml was:." + xml + "\n\n" + err;
console.log(message);
res.status(500);
res.send(message);
}
const documentType = json.Document.DocumentType;
if (documentType == 945) {
const shipment = json.Document.Shipment[0];
const shipment_header = shipment.ShipmentHeader[0];
const addresses = shipment.Addresses[0].Address;
const order_header = shipment.Orders[0].Order[0].OrderHeader[0];
const cartons = shipment.Orders[0].Order[0].Cartons[0].Carton;
const unique_id = shipment_header.ShipmentID[0];
found_document_promise = document_is_redundant(AMS_945, unique_id);
found_document_promise.then(function (found_document) {
if (found_document != null) {
console.log("Redundant document. Perhaps a Pair Eyewear engineer was running a test?");
res.status(200);
message = "Redundant document. Perhaps a Pair Eyewear engineer was running a test? documentType: " + documentType;
res.send(message);
} else {
new AMS_945({
shipment_header : shipment_header,
addresses : addresses,
order_header : order_header,
cartons : cartons,
unique_id : unique_id
})
.save()
.then(function () {
// console.log("saved AMS_945");
message = "saved AMS_945";
res.status(200);
res.send(message);
})
.catch(function (err) {
message = "error when saving AMS_945 to database: " + "\n\n" + err;
console.log(message);
res.status(500);
res.send(message);
});
}
})
.catch(function(err) {
message = "error when checking for a redundant AMS_945 document: " + "\n\n" + err;
console.log(message);
res.status(500);
res.send(message);
});
// down at the bottom I have some generic catch-all:
res.status(200);
res.send("Catch all response.")_;
If I don't have the catch all response at the end then the connection simply hangs until the 30 second timeout is hit, and then I get 504 Bad Gateway.
With the catch-all response at the bottom, thankfully I don't get the timeout error, but I do get an error "Sending headers after response was sent" because at some point the database code returns and tries to send its response, but this is long after the Express route function has completed and sent that generic catch-all code that I have at the bottom.
I'd be happy to get rid of the catch-all res.send() and just have the res.send() inside the database code, but that never seems to return.
So how to I get the Express route function to wait till the database code has returned?
I dont know what to say, i think code says everything. i just want to render something and this error appears, but i dont know why. I tried so many solutions from stackoverflow, and others that i couldn't count them all. Im using mysql2 lib. Please help.
router.get("/:link", function(req, res){
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
function (err, result, rows, fields){
Object.keys(result).forEach(function(key) {
var row = result[key];
console.log(row.link)
if(row.link = req.params.link) {
res.send(row.link + row.views + rows.created)
return;
} else {
res.send("URL does not exist")
return;
}
});
});
});
just because you send it in foreach you get that error .
do you want to send it just one time and first item of db ? if yes use this :
router.get("/:link", function (req, res) {
var i = 0;
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
function (err, result, rows, fields) {
Object.keys(result).forEach(function (key) {
var row = result[key];
console.log(row.link)
if (row.link = req.params.link) {
if (i === 0) {
res.send(row.link + row.views + rows.created)
i = 1;
}
} else {
if (i === 0) {
res.send("URL does not exist")
i = 1;
}
}
});
});
});
A service that sends your data. You run into problems when you try to return your data in a loop. The good thing about this is to come back after you get all the result you will get from the loop. You can use asynchronous structure for this. Maybe you can try this.
router.get("/:link", (req, res) => {
let myObject = {};
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
async(err, result, rows, fields) => {
await Object.keys(result).forEach(function(key) {
var row = result[key];
if(row.link = req.params.link) {
myObject[key] = {link: row.link, views: row.views, created: rows.created}
return;
} else {
myObject['error'] = 'URL does not exist';
return;
}
});
await res.send(myObject);
});
});
I'm trying to update objects in an array from my Mongo database and saving the updated values back to the database. However, after calling save on the data I changed, nothing gets updated to the database.
router.post('/:poll*', (req, res)=>{
var url = '/poll' + req.url;
Poll.findOne({link: url}, (err, data)=>{
if(err){
throw err;
}
else{
var theValue;
for(var key in req.body){
if(key === 'opt'){
theValue = req.body[key];
}
}
var voteCount;
if(data.voters[0] == null){
data.voters[0] = {};
data.voters[0][req.user.username] = theValue;
data.options[0][theValue] = data.options[0][theValue] + 1;
}
else{
if(data.voters[0][req.user.username] != theValue){
var previous = data.voters[0][req.user.username];
data.voters[0][req.user.username] = theValue;
data.options[0][theValue] = data.options[0][theValue] + 1;
if(previous){
data.options[0][previous] = data.options[0][previous] - 1;
}
}
}
}
console.log(data.voters)
data.save(function(err){
if(!err){
console.log('saved');
}
else{
console.log('error');
}
});
res.send("Your input has been submitted.");
});
});
I also put console.log(data that I've changed) right before the (data that I've changed).save(function( ...)) code.
What can I do to fix this?
EDIT (HOW I SOLVED THIS):
It appears that Mongo does not save my array when I access the array and modify them like:
data.voters[0][username] = value;
Instead I resorted to popping out the object, modifying it, and then pushing it back in. Now I see the values being properly saved/updated in the database.
Aka something like:
var obj = data.voters.pop();
obj[username] = value;
data.voters.push(obj);
Anyone know why this happens?
Im learning bacon.js
i wrote test script like that.
var bacon = require('../nodejs/node_modules/baconjs').Bacon;
function TestClass(URL,port,username,password)
{
this.funcCheck = function( data , rescallback)
{
console.log(" data: " + data);
if(data.length > 4)
{
rescallback(null,data.substring(0, 2));
}
else
{
console.log("Calling callback with error data: " + "error");
rescallback("Too short",null);
}
}
}
var tclass = new TestClass();
var names = ['Geroge','Valentine', 'Oz','Nickolas'];
var results = [];
var read = bacon.fromNodeCallback( tclass.funcCheck, bacon.fromArray(names) )
stream.onValue(function(value)
{
console.log(" onValue " + value);
results.push( value);
});
stream.onError( function(error)
{
console.log(" OnError " + error);
// results.push( err);
});
console.log(" results " + results);
The problem is that onError never get called, despite the fact that "Oz" is less than 4 characters and i know that rescallback get called with 1 parameter that is not null.
I can see log printout.
The other thing is that if i change defition of onError and put it before onValue, onError will be called exactly 1 time, and onValue will never be called.
Im sure im missing something very basic but i dont know what exactly.
What im doing wrong?
Thanks