Node.js app crash after one request sent - javascript

I send a POST request from front end and I declare my route like below
router.post('/login', function(req, res, next) {
});
My app crash and I got Error: Can't set headers after they are sent.
Here is my full code for route : http://pastebin.com/EnfXu8Vm

You cannot send twice the reply to the same request.
So the code here:
if(result){
res.json({msg:0})
}
res.json({msg:1})
You can probably want to do
if (result) {
res.json({msg:0});
} else {
res.json({msg:1});
}
or, to be more similar to your original code (so without else):
if (result) {
return res.json({msg:0});
}
res.json({msg:1});

Related

Node.JS returns a 404 on interrogation of an endpoint that actually exists

I have a web application, started by a previous company, written in Angular.JS. The application exposes a request towards the back-end (written in Node.JS+Express) to gather some data required to fill a table. Specifically, this is the request that the application sends everytime the user enters in the page that holds the table (The config variable holds the access token).
return $http.get(API + '/api/myPath/for/Having/Data', config).then(handleSuccess, handleError);
handleSuccess and handleError are so defined
handleSuccess: function (res) {
debugger;
var deferred = $q.defer();
res.data.success ? deferred.resolve(res.data) : deferred.reject(res.data.message);
return deferred.promise;
},
handleError: function (error) {
return {
success: false,
message: error
};
}
In my back-end I've put an a listener to whatever gets called with the "/api" prefix, like this
app.use('/api', authorization.validateToken);
And another listener, that should work only if there is no match (written at the very end of the file that handles all the general inquiries of the app)
app.all('*', (req, res) => {
console.log('Hi, Stack Overflow!');
res.send({
success: false,
status: 404,
message: 'Invalid Uri Resource'
});
});
And, lastly, this is the endpoint that should get called in the back-end from Angular.js
app.get('/api/myPath/for/Having/Data', something.somethingToCall);
Here's the funny part: for a reason that I still have to understand, Angular.JS calls that endpoint twice, resulting in one failing procedure (404) and another one that goes smoothly (200).
The operation flow should be like this: Angular calls the back-end --> Node checks the validity of the token --> executes operation if everything goes okay.
The operation is called twice (seen thanks to the Visual Studio Code debugger and Chrome's Network Monitor) and, even though the token's validation process is correctly executed everytime, the first time the next() function will hold the app.all() listener.
Also,even before I start debugging the first request that is sent out, the JavaScript console on Google Chrome warns me that there has been an error such as like "Cannot read property 'data' of undefined", meaning that the request gets executed twice with the first time returning a 404.
exports.validateToken = (req, res, next) => {
console.log(`check user here`);
// next();
var token = //I take the token
console.log(token);
if (token) {
jwt.verify(token, require('../../secret'), (err, decoded) => {
if (err) {
res.send({
success: false,
status: 500,
tokenExpired: true,
message: "Effettua nuovamente l'accesso"
});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.send({
success: false,
status: 406, // Fprbidden
message: 'User not Authenticated'
});
}
};
Does anybody know how to help me somehow?
EDIT: this is an example of how Chrome's sees both requests. The screenshot, in particular, refers to the first one that gets called and produces the 404
The CORS is handled in the back-end like this
app.use(function (req, res, next) {
if (req.headers.origin && (req.headers.origin.match("http:\/\/somewebsite.com.*") || req.headers.origin.match("http:\/\/localhost:8010") )) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
}
next();
});
Also, I'm adding the endpoint that needs to be called. This also exploits MongoDB + Mongoose for querying the DataBase and return stuff to the front-end. The parameters that I'm passing are pageSize (how many elements per page) and the current page number
exports.getAds = (req, res) => {
var criteria = req.body || {};
var pageSize = criteria['pageSize'] ? Number(criteria['pageSize']) : undefined;
var pageNumber = criteria['pageNumber'] ? Number(criteria['pageNumber']) : undefined;
var sort = criteria.sort || { createdAt: 'desc' };
if (criteria.customerName) criteria.customerName = { $regex: `.*${criteria.customerName}.*`, $options: 'i' };
if (criteria.spentEuros) criteria.spentEuros.$gte = criteria.spentEuros;
if (criteria.referralMail) criteria.referralMail = { $regex: `.*${criteria.referralMail}.*`, $options: 'i' };
console.log(criteria);
var columns = "customerName duration spentEuros";
if (pageSize && pageNumber) {
Adv.paginate(criteria, {
page: pageNumber,
limit: pageSize,
select: columns,
sort: sort
}, function (err, result) {
if (!err) res.status(200).send({ success: true, data: result });
else res.status(500).send({ success: false, message: err });
});
} else {
Adv.find(criteria)
.select(columns)
.sort(sort)
.exec(function (err, result) {
if (!err) res.status(200).send({ success: true, data: result });
else res.status(500).send({ success: false, message: err });
});
}
};
EDIT2: Solution to the question: adding an app.options listener in the back-end (as pointed out by #slebetman), alongside with the already existing app.get one, solved the issue
Here's the funny part: for a reason that I still have to understand, Angular.JS calls that endpoint twice...
That sounds a lot like the browser sending a CORS preflight OPTIONS request, followed by a GET. Check the HTTP verb being used, and be sure you're handling OPTIONS (not just GET) if you need to support CORS on your endpoint. (If you're not expecting this to be a cross-origin request, check the origin of the page relative to the origin of the API call, something [protocol, port, domain] seems to be different — if it's an OPTIONS call.)

Why adding a get parameter forces middleware execution

I have got a middleware like this
// route middleware to verify a token
app.use(function(req, res, next) {
console.log(req.baseUrl);
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
This route http://localhost:8080/verifyAccount doesn't responds as No token provided
app.get('/verifyAccount', function (req, res) {
res.json({ message: 'Welcome to verify account!' });
});
But the following route http://localhost:8080/verifyAccount?id=123 does:
app.get('/verifyAccount/:id', function (req, res) {
res.json({ message: 'Welcome to verify account!' });
});
The middleware code is at the bottom in the code file and the get paths are upwards
What is that concept behind?
Why adding a get parameter forces middleware execution?
Just found that if I call it like this http://localhost:8080/verifyAccount/id=123, it properly returns Welcome to verify account!
Found that the issue was in the way by which the route was getting called. Thanks to Thomas Theiner for the help.
The request with query parameter ?id=123 does not match with /:id. It should be called as verifyAccount/123 instead.
Since, the route ?id=123 did not matched any of the path. Hence, was finally reaching the middleware for execution
The position determines the parameter, not the name. The name is only used inside node code to reference the parameter.
For multiple parameters, we'll have multiple slashes like verifyAccount/:id/:otherParameter which would be called using verifyAccount/123/234

Update content of page from a POST request in Express

I had a problem where I tried to update the contents of the web page through a POST request that was done through a form but the problem is that the variables were set to the global scope and every time that I refreshed the page the content was still there. Somebody explained what was the issue and told me that I could "...make the post return a JSON object with the data instead of doing a redirect. Then do the POST async from the client and display the data." Here is the code that I'm using on:
Express:
var data;
var url;
/* GET home page. */
router.get('/', (req, res, next) => {
res.render('index', { 'data': data});
});
/* POST HANDLER */
router.post('/link', function(req, res, next) {
var pattern = /^((http|https|):\/\/)/;
url = req.body.link;
if (!pattern.test(url))
{
url = "http://" + url;
bitly.shorten(url)
.then(response => {
data = response.data.url;
res.redirect("/");
});
}
});
And I'm using jQuery for the POST request:
$('#shortLink').on('click', () => {
$.ajax({
type: 'POST',
url: '/link',
data: {link: $('#linkInput').val()},
success: data => {
console.log(data);
}
});
});
What I want to do is get the value of an input, send it to the POST handler on Express and then pass that information back to the home page without having to get out of the page; like a common Ajax request. Can somebody elaborate on what I was suggested to do, above? Or give me another solution.
There's no rule that says you have to redirect after handling a post request. Just send the data back via res.json({'url': response.data.url}), and then in your $.ajax success handler it will be available via data.url.

Add an error page to this express app?

I'm building an express app, a basic twitter interface.
I want to add an error page to the application, so that if anything goes wrong with the routes the user will see a friendly message rendered, instead of the default error code.
Here is a snippet of my code:
//Tell app to render template
app.get('/', function(req, res){
if(!error){
res.render('index', {
myName: myName,
profileImage: profileImage,
screenName: screenName,
followerCount: followerCount,
dateTweeted: dateTweeted,
tweetContent: tweetContent,
noOfRetweets: noOfRetweets,
noOfLikes: noOfLikes,
});
}
});
Why can't I just do this?
else{
res.send('sorry, bro, page not found!);
}
Or do I need to do something with passing the error to the 'next’ handler? I can't get my head around how that works.
Would really appreciate some help please!
Your question is not very specific but I assume you will get some errors during manipulation.
Then you can send type of error like this after getting error
//Tell app to render template
app.get('/', function(req, res) {
if ("not error") {
//You can do whatever you want
res.status(200).send({message:"Success message"})
}else{//If error you can choose your error code with relevant message
res.status(400).send({message:"Bad request error"});
//OR
res.status(404).send({message:"Not found error"});
//OR
res.status(401).send({message:"Unauthorization error"});
//OR
res.send(500).send({message:"Any server side erorr"});
}
});
You can build a custom middlewarethat does this for you...
function errorMiddleware(req, res, next) {
// implement some logic, do your check...
let thereAreErrors = /* ? */ false;
if(!thereAreErrors) {
return next();
}
return res.status(400).end();
}
function indexRouteCtrl(req, res) {
return res.render('index');
}
app.get('/'/, errorMiddleware, indexRouteCtrl);

node.js & express: for loop and `app.get()` to serve articles

I'm working on a node js express application that uses app.get() to serve the different web addresses. So app.get('/',{}); serves the home page, app.get('/login'{ }); serves the login page, etc.
Is it good practice to programatically serve pages using a for loop, like in this example?
var a = ["a", "b", "c"];
a.forEach(function(leg) {
app.get('/' + leg, function(req, res){
res.render('index.ejs', {
word : leg
});
});
});
index.ejs is just
<p><%= word %> </p>
So that site.com/a, site.com/b, and site.com/c are all webpages.
I want to utilize this to run .foreach() on a list of all of the article titles (coming from a database of stored articles).
EDIT: The website allows users to submit posts, which become "articles" in a database. I want this loop to route to new submitted articles after they've been posted. If I want to do app.get('/' + newPageName); for user submitted pages AFTER I've already started the server with node server.js, how is that achieved?
Make use of middlewares to better handle the requests. I assume you will have tens/hundreds of posts, adding routes for them like what you've done, is not so elegant.
Consider the following code; I am defining routes of /posts/:legId form. We will match all requests to this route, fetch the article and render it.
If there are handful of routes to be defined you could make use of regular expression to define them.
// dummy legs
var legs = {
a: 'iMac',
b: 'iPhone',
c: 'iPad'
};
app.get('/posts/:leg', fetch, render, errors);
function fetch(req, res, next) {
var legId = req.params.leg;
// add code to fetch articles here
var err;
if (!legs[legId]) {
err = new Error('no such article: ' + legId);
}
req.leg = legs[legId];
next(err);
}
function render(req, res, next) {
res.locals.word = req.leg;
res.render('index');
}
function errors(err, req, res, next) {
console.log(err);
res.locals.error = err.message;
// render an error/404 page
res.render('error');
}
Hope this helps, feel free to ask me any questions you may have.
No, you should not be generating route handlers like that. This is what route parameters are for.
When you start a path component (folder) with a : in an Express route, Express will match any URL that follows the pattern automatically, and place the actual value in req.params. For example:
app.get('/posts/:leg', function(req, res, next) {
// This will match any URL of the form /post/something
// -- but NOT /post/something/else
if (/* the value of req.params.leg is valid */) {
res.render('index.ejs', { word: req.params.leg });
} else {
next(); // Since the user requested a post we don't know about, don't do
// anything -- just pass the request off to the next handler
// function. If no handler responds to the request, Express
// defaults to sending a 404.
}
});
In the real world, you'd probably determine if the leg param is valid by doing a database lookup, which entails making an async call:
app.get('/posts/:leg', function(req, res, next) {
db.query(..., req.params.leg, function(err, result) {
if (err) next(err); // Something went wrong with the database, so we pass
// the error up the chain. By default, Express will
// return a 500 to the user.
else {
if (result) res.render('index.ejs', result);
else next();
}
});
});

Categories