My setup is as follows:
posting to /register will take the arguments and register a user via passport and mongoose. If this returns an UserExistsError the server sends this info to the client (via http error handling).
However the server also displays a 500 server error which should not occur.
This is because of the next() which as far as I understand routes the client to /register. /register itself does not exists as a page (only as the postadress as stated in the code)
So my question is: How to handle the response to not be an error or supress it? Can I use something else instead of next() to stop the redirect to /register? I just want the server to stop doing anything/going out of that function at that point.
Code:
app.post('/register', function(req, res, next) {
console.log('server registering user');
User.register(new User({username: req.body.username}), req.body.password, function(err) {
let tempstring = ""+err;
if(tempstring.indexOf("UserExistsError") !== -1){
return next(err); //get out of the function and into normal operation under '/'
}
});
});
This topic is bugging me and I might just missunderstand something trivial.
Even if /register is a post only route you still need to send a response. If you don't send a response of some kind, the request will hang and eventually timeout in the browser. I would suggest sending a json response like so.
app.post('/register', function(req, res, next) {
console.log('server registering user');
User.register(new User({username: req.body.username}), req.body.password, function(err) {
let tempstring = ""+err;
if(tempstring.indexOf("UserExistsError") !== -1){
return next(err); //get out of the function and into normal operation under '/'
}
res.json({message: 'message here'});
});
});
This will send a 200 OK reponse with some json in the body.
If you just want to pass the request down the line you need to call next without an err object like so.
app.post('/register', function(req, res, next) {
console.log('server registering user');
User.register(new User({username: req.body.username}), req.body.password, function(err) {
let tempstring = ""+err;
if(tempstring.indexOf("UserExistsError") !== -1){
return next(err); //get out of the function and into normal operation under '/'
}
//call next without an error
next();
});
});
I am not sure if this is what you are trying to achieve, but if there is no route that matches it will just go to an error 500.
Related
am trying to have a router.get inside router.post, I need to compare the information provided by the user to the one in the database and then post it if it does not exists. The problem is the router.get is never reached. No errors, and postman keeps "sending request" with no end. Is it possible to have a router.get inside router.post?, if yes how?, If no, how do I get info from router.get to pass to router.post? I need to run the api from ..../new, and do all the work from there. Thanks in advance
//register
router.post('/new', (req, res, next)=>{
console.log("jumped out");
var user_id, password0, password1;
user_id = req.body.user_id;
password0 = req.body.password0;
password1 = req.body.password1;
console.log(password1);
//retrieving usernames to check if it exists
router.get('/accounts', (req, res, next)=>{
console.log("in here");
detail.find(function(err, accounts){
//looping through usernames
for (var i=0; i<accounts.length; i++){
if (accounts[i].user_id === user_id){
res.json({msg: 'Username taken'});
}
else if(i == (accounts.length-1)){
if (password0 === password1){
let newAccount = new account({
user_id: this.user_id,
password: this.password0
});
newAccount.save((err, account)=>{
if(err){
res.json({msg: 'failed to create account'});
}
else{
res.json({msg: 'Account created successfully'});
}
});
}
else if (password0 !== password1){
res.json({msg: 'Password mismatch'});
}
}
}
//res.json(accounts);
});
});
});
I think the issue here is that you have a misunderstanding about what router.get is doing. It sounds like you want to accept a POST request from the user, then make a get request to check if the data already exists, and if not, then update your database.
If this is the case, the inner GET should actually use something like axios to make a request. You can make an axios request like this:
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
If you are actually trying to accept a second request from the user after the POST, then you should NOT do that. Users should only make one request for one action. You'll run into all kinds of issues down that path.
Turns out you can still retrieve data with only .POST, without having .GET. Removing the line having router.get, fixed the problem.
router.get('/accounts', (req, res, next)=>{
im new in node js, when im trying to sent data to database from react, on my node js got error 'can't set headers after they are sent. i've been searching for same like this problem, but it doesnt help me to solve this problem
i've tried using writeHead on the post(the second one)
but it doesn't help, is it bcs i sent the same image? i only got problem with sending the same image
app.post('/list', function(req, res){
const {namaCabor, descCabor, imgCabor } = req.body;
connectDb.collection('listCabangOlahraga').insertOne(req.body, function(err, res){
console.log(res.insertedCount +'data inserted');
});
res.send(req.body);
});
app.post('/add', function(req, res){
const {categoryName, categoryDesc, categoryImage, namaCabor, descCabor, imgCabor } = req.body;
connectDb.collection('listCategoryCabangOlahraga').insertOne(req.body, function(err, res){
console.log(res.insertedCount +'data inserted');
if(err) throw err;
});
res.writeHead(200, {'Content-Type' : 'application/json'});
res.end(JSON.stringify(req.body));
});
Quick analysis : Your code involves writing to MongoDB collection. It has an async callback. I guess the res.write() / res.send() should be included inside the callback?
If not, they get executed even before the DB operation is complete and we won't know if it succeeded or not.
app.post('/list', function(req, res){
const {namaCabor, descCabor, imgCabor } = req.body;
connectDb.collection('listCabangOlahraga').insertOne(req.body, function(err, res){
console.log(res.insertedCount +'data inserted');
// <----- Handle the error here and print response accordingly.
});
res.send(req.body); //Move this inside callback. Return error response if err encountered.
});
app.post('/add', function(req, res){
const {categoryName, categoryDesc, categoryImage, namaCabor, descCabor, imgCabor } = req.body;
connectDb.collection('listCategoryCabangOlahraga').insertOne(req.body, function(err, res){
console.log(res.insertedCount +'data inserted');
if(err) throw err;
// <----- Handle the error here and print response accordingly.
});
res.writeHead(200, {'Content-Type' : 'application/json'}); // Move this inside callback.
res.end(JSON.stringify(req.body)); //Write response from the callback.
});
Basically the error appears because res.write() / res.send() is called before setting the header
Also it's a good idea to have the res object renamed in either of the places (maybe the res (result) object in the MongoDB write callback can be renamed to avoid potential confusions with the res(response) object of the express route)
I guest that you have an error handler. In the /add route, it throws an error and your error handler will catch it. So you tried to set response header in both /add handler and error handler. The second handler will throw the error 'can't set headers after they are sent because response was already sent in the first handler(may be error handler or /add handler)
This error occur whenever node try to send http response after it has been sent,
e.g
{if(true){
res.send({})
}
res.send({
// this will throw that error because the pool is already close because the first res.send was executed.
});
}
However, in you case there is no such scenario visible in the code you posted,
but in in server.js, a method executed might have sent the response to the frontend and the pool is closed before the route controller finish processing causing the res.send or res.end in your code to throw the error.
So try to check other portion of your code base to debug this error.
I'm not really sure why I'm getting this error. It's a simple API built on express.js to be able to add and remove posts. The error occurs when I trigger the delete router. I've read that the error typically happens when there are two callbacks, however, I don't seem to be able find any double callbacks.
_http_outgoing.js:344
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:344:11)
at ServerResponse.header (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:718:10)
at ServerResponse.send (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:163:12)
at ServerResponse.json (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:249:15)
at /Users/bounty/Projects/_learning/react-express/server/routes/posts.js:86:9
at nextTickCallbackWith0Args (node.js:452:9)
at process._tickCallback (node.js:381:13)
Here is my posts.js router:
module.exports = function(router) {
var Post = require('../models/post.js');
// middleware for the api requests
router.use(function(req, res, next) {
// do logging
console.log('something is happening.');
next(); // make sure we go to our next route and don't stop here
});
// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
// all routes here
// routes that end in /posts
router.route('/posts')
// create a Post (accessed at POST http://localhost:7777/api/posts)
.post(function(req, res) {
var post = new Post();
post.postTitle = req.body.postTitle; // set the post name (comes from request)
// save post and check for errors
post.save(function(err) {
if (err)
res.send();
res.json({ message: 'post created!' });
});
})
// get all Posts (accessed at GET http://localhost:7777/api/posts)
.get(function(req, res) {
Post.find(function(err, posts) {
if (err)
res.send();
res.json(posts);
});
});
// routes that end in /posts for specific id
router.route('/posts/:post_id')
// get the post with that id
.get(function(req, res) {
Post.findById(req.params.post_id, function(err, post) {
if (err)
res.send(err);
res.json(post);
});
})
// update the post with that id
.put(function(req, res) {
Post.findById(req.params.post_id, function(err, post) {
if (err)
res.send(err);
post.postTitle = req.body.postTitle;
// save the post
post.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'post updated!' });
});
});
})
// deletes the post with that id
.delete(function(req, res) {
Post.remove({
_id: req.params.post_id
}, function(err, post) {
if (err) {
res.send(err);
}
res.json({ message: 'post deleted!' });
});
});
}
You need to add the 'return' so that you don't reply twice.
// save post and check for errors
post.save(function(err) {
if (err) {
return res.send();
}
res.json({ message: 'post created!' });
});
That particular error message is pretty much always caused because of a timing error in the handling of an async response that causes you to attempt to send data on a response after the response has already been sent.
It usually happens when people treat an async response inside an express route as a synchronous response and they end up sending data twice.
One place I see you would get this is in any of your error paths:
When you do this:
// save post and check for errors
post.save(function(err) {
if (err)
res.send();
res.json({ message: 'post created!' });
});
If post.save() generates an error, you will do res.send() and then you will do res.json(...) after it. Your code needs to have a return or an else so when there's an error you don't execute both code paths.
So, this can happen in Express when attempting to send res.end twice which res.send and res.json both do. In your if(err) block you'll want to return res.send() as res.send runs asynchronously and res.json is getting called as well. I'm wondering if you're getting an error in your delete route? Hope this helps.
Best!
You are using res.send() or res.json() twice in the same request
this send the headers first, followed by body of the response and then headers again.
req.next is usually not a function, next is rather passed as a third argument of the middleware. Use that if you want to drop to the next middleware. (assuming you are using Express framework)
Just for the sake of completeness I will also mention that:
Sometime problem may be in a the middleware you may be using by calling
app.use.
After checking for obvious errors as mentioned in previous answers:
You should remove all the app.use statement then reintroduce them one by one, to find problematic module.
If you are using res.send() inside any loop, then you need to break it after the use of res.send(). So that it won't allow resetting of the res headers again and again.
for e.g :
for(){
if(){
res.send();
break;
}
else(){
res.send();
break;
}
}
In my case this is the problem and I solved it like this.
Hope it may help someone in future.
Thanks
For a quick fix you can just check res.finished before calling res.send():
if (!res.finished)
res.send()
I am using angularjs and jade templating for my client side. And node and express on the server side. I recently added passport authentication and have the local strategy working fine when the login is attempted with valid credentials. It's the error cases that I'm having trouble with. I have implemented the custom callback to get my error messages to bubble back to the client. Now I get the error messages but the status gets set to 200 somewhere, instead of the 401 I expect.
// login route
app.post("/api/login", function(req, res, next) {
passport.authenticate("local", function(err, user, info) {
if (err) {
return next(err);
}
if (user === false) {
res.status = 401;
res.send(info.message);
} else {
res.json({success:"User logged in"});
}
})(req, res, next);
});
and this is the angular controller that submits the login request:
var loginObj = {email: $scope.login.email,
password:$scope.login.password};
$http.post("/api/login", loginObj)
.success(function(data, status) {
console.log("successful login");
console.log("data = " + JSON.stringify(data));
console.log("status = " + status);
})
.error(function(error) {
console.log("error logging in.");
console.log("error = " + JSON.stringify(error));
$scope.error = error;
});
So, suppose I have a password mismatch... the error in my validation routine sets my error message to "Invalid password, please try again." But what I see on the console is:
successful login
data = "Invalid password, please try again."
status = 200
I'm not resetting the status anywhere else. How is it getting set to 200? What am I doing wrong? Sorry, I'm a relative newbie to node and angular, so any help would really be appreciated. I know I'm not understanding some key point, I just can't figure out what. Thanks!
It's res.status(401), not res.status = 401.
res.status() is a function.
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 });
}
});