I'm using nodejs, express 4 and everyauth for social network authentication. I'm having some problem where I click Accept from Google and and redirects back to my / I get
_http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
I'm using electrolyte as a DI as well but I don't think that would be an issue. Here's my code
index.js
var express = require('express'),
http = require('http'),
bootable = require('bootable');
var app = bootable(express());
app.phase(bootable.initializers('etc/init', app));
app.phase(bootable.routes(__dirname + '/routes.js', app));
app.phase(function listen(done) {
http.createServer(app).listen(process.env.PORT || 3000, function(err) {
if (err) { return done(err); }
var addr = this.address();
console.log('server listening on http://' + addr.address + ':' + addr.port);
done();
});
});
module.exports = app;
routes.js
var IoC = require('electrolyte');
module.exports = function routes() {
this.get('/', IoC.create('handlers/homepage'));
this.get('/api/foursquare', IoC.create('handlers/api/foursquare'));
//this.get('/auth/google/callback', IoC.create('handlers/auth/google_auth_callback'));
}
handlers/homepage
exports = module.exports = function() {
function render(req, res, next) {
res.render('index');
};
return [render];
};
google.js
var everyauth = require('everyauth'),
GoogleUser = require('../../app/models/google_user'),
mongoose = require('../../app/db/mongo');
require('dotenv').load();
module.exports = function() {
everyauth.google
.appId(process.env.GOOGLE_CLIENT_ID)
.appSecret(process.env.GOOGLE_CLIENT_SECRET)
.scope('https://www.googleapis.com/auth/plus.login') // What you want access to
.handleAuthCallbackError( function (req, res) {
// If a user denies your app, Google will redirect the user to
// /auth/facebook/callback?error=access_denied
// This configurable route handler defines how you want to respond to
// that.
// If you do not configure this, everyauth renders a default fallback
// view notifying the user that their authentication failed and why.
})
.findOrCreateUser( function (session, accessToken, accessTokenExtra, googleUserMetadata) {
console.log('come back from google');
console.log(session);
// find or create user logic goes here
// Return a user or Promise that promises a user
// Promises are created via
// var promise = this.Promise();
/*
var googleUser = new GoogleUser(mongoose);
var johndoe = new googleUser({
accessToken: 'accessToken',
expires: new Date(),
refreshToken: 'refreshToken',
email: 'john#doe.com',
createdAt: new Date()
});
var promise = this.Promise();
promise.resolve(johndoe);
return promise;
*/
}).redirectPath('/');
};
mongo.js
var mongoose = require('mongoose')
require('dotenv').load();
var uristring = process.env.MONGOLAB_URI;
mongoose.connect(uristring, function (err, res) {
if (err) {
console.log ('ERROR connecting to: ' + uristring + '. ' + err);
} else {
console.log ('Succeeded connected to: ' + uristring);
}
});
exports = module.exports = mongoose
You will get Can't set headers after they are sent. if your code is like this
if (err) {
res.send(err);
}
else if(!user) {
res.send(info);
}
res.send(user);
In the above code, if user is undefined it sends the info as response and it again comes to next statement i.e res.send(user);. So it cannot set headers to the response which is already sent.
You can prevent this by
if (err) {
res.send(err);
}
else if(!user) {
res.send(info);
}
else {
res.send(user);
}
or
if (err) {
return res.send(err);
}
else if(!user) {
return res.send(info);
}
return res.send(user);
Related
I saw a similar question posted Here, but they are using MEAN-Stack.
I am currently just using a 'setTimeout' function to wait a few seconds before requesting new data from the server using a fetch api to give it time to update but this doesnt feel like the right way to do it. Is there a simple way for the front-end to update only after the database is updated in Express? I am new to Node please forgive me.
app.js:
const express = require('express');
const app = express();
const mysql = require('mysql');
let viewData = {
//html data
}
var pool = mysql.createPool({
connectionLimit : 10,
host: "localhost",
port: 3306,
database: 'testing',
user: "root",
password: "pass"
});
function sql(type) {
if(type == 'select') {
//Select query here
}
if(request == 'addRow') {
//Insert query here
}
}
app.get(`/`, function (req, res) {
res.sendFile('./views/index.html', {root: __dirname});
})
app.post('/api/add', function(req, res){
res.setHeader('Content-Type', 'application/json');
sql('addRow')
});
app.get('/api/viewData', function (req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(viewData));
})
index.js:
function loadData() {
fetch('/api/viewData')
.then(z => z.json())
.then(a => {
//update html
})
}
function postData(a) {
fetch('/api/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
//data to send to app.js
})
}).then(setTimeout(function(){loadData();}, 3000))
}
You should use async and await function
Example: After async/await
async function fun1(req, res){
let response = await request.get('http://localhost:3000');
if (response.err) { console.log('error');}
else { console.log('fetched response');
}
The complete code of our example is shown below:
npm install express jsonschema body-parser promise-mysql
var express = require('express');
var bodyParser = require('body-parser')
var app = express();
var validate = require('./validate')
var mysqlConnection = require('./connectionShare');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const addItem = function(item, connection){
console.log("Adding Item");
return new Promise(function(resolve, reject){
connection.query("INSERT INTO product SET ?", item)
.then(function(result){
resolve(item.seller);
}).catch(function(error){
reject(error);
});
})
}
const findOrCreateUser = function(user,connection){
console.log("Finding User");
return new Promise(function(resolve,reject){
connection.query("SELECT * FROM user WHERE email=" + connection.escape(user.email))
.then(function(results){
if(results.length == 1){
resolve(results[0].id)
} else {
connection.query("INSERT INTO user SET ?", user)
.then(function(results){
resolve(results.insertId);
});
}
}).catch(function(error){
reject(error);
})
})
}
const selectUserItems = function(userID,connection){
console.log("Selecting Items " + userID);
return new Promise(function(resolve,reject){
connection.query("SELECT * FROM product WHERE seller = " + connection.escape(userID))
.then(function(results){
resolve(results);
}).catch(function(error){
reject(error);return;
});
})
}
app.post('/add/product', validate.JsonValidation, mysqlConnection.getConnection, async function(req,res){
var connection = req.connection;
var item = {
name: req.body.name,
price: req.body.price,
width: req.body.width,
height: req.body.height,
added: req.body.added,
image: req.body.image
};
var user = {
username: req.body.seller.username,
email: req.body.seller.email,
votes: req.body.seller.votes
};
try {
item.seller = await findOrCreateUser(user,connection);
var user_id = await addItem(item,connection);
var items = await selectUserItems(user_id, connection);
connection.connection.release();
res.status(200).json(result);
} catch(error) {
res.status(500).end(error);
}
});
process.on('uncaughtException', error => console.error('Uncaught exception: ', error));
process.on('unhandledRejection', error => {console.error('Unhandled rejection: ', error));
app.listen(8000, function () {
console.log('App listening on port 8000')
});
I am trying to build a registration page with nodeJS and firebase.
When the user gets a registration error the page reloads and the error is shown to the user.
Then if the user tries to register, he gets to the desired page but I get:
Error: Can't set headers after they are sent.
After several hours of researching, I think I have understood the problem.
My code is probably already in the Body or Finished state, but some function tried to set a header or statusCode
With this knowledge, I tried to solve this error but I don't get it done.
Here is my user.js(controller) code:
var express = require('express');
var firebase = require('firebase');
var router = express.Router();
var User = require('../models/user');
var errors;
var errorMessage;
/**
sign up
*/
//sign up page route
router.get('/', function (req, res, next) {
console.log(errors);
if (errors) {
//Handle the errors and show them to the user
res.render('signUp', {error: true, errorMessage: errorMessage});
}else {
res.render('signUp', {title: 'Sign up', user: false, login: true});
}
});
// sign up request
router.post('/', function(req, res, next){
console.log(req.body.user.email + ' made a registration request');
console.log('Passwword: ' + req.body.user.password);
var email = req.body.user.email;
var password = req.body.user.password;
//reference to the firebase database
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
if (error) {
console.log(error.code);
console.log(error.message);
errorMessage = error.message;
errors = true;
res.redirect('/');
}
});
//if user is logged in redirect to homepage
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
res.redirect('/users/home');
console.log('User: ' + user.email + ' has logged in');
}
});
});
module.exports = router;
I hope someone can help me.
If you are using version 7+ of node, you can use async/await like I did in the following snippet:
var express = require('express');
var firebase = require('firebase');
var router = express.Router();
var User = require('../models/user');
var errors;
var errorMessage;
async function createUser(email, password) {
return new Promise(resolve => {
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
if (error) {
console.log(error.code);
console.log(error.message);
errorMessage = error.message;
errors = true;
return resolve(true)
}
resolve(false)
});
})
}
async function authChange() {
return new Promise(resolve => {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log('User: ' + user.email + ' has logged in');
return resolve(true)
}
resolve(false)
});
})
}
// sign up request
router.post('/', async function(req, res, next) {
console.log(req.body.user.email + ' made a registration request');
console.log('Passwword: ' + req.body.user.password);
var email = req.body.user.email;
var password = req.body.user.password;
//reference to the firebase database
let error = await createUser(email, password)
if (error) {
res.redirect('/');
} else {
let logged = await authChange()
if (logged) {
res.redirect('/users/home');
}
}
});
module.exports = router;
I have a aplication where the user can take some pictures and send to the database, just as simple as that.
Everytime the user login he get a token, if everything fine with the token(he doesn't need to login).
I followed this tutorial to do the jwt authentication, now i want to check on every request except(/login / register) that token and decode it to get the user info ( i am just saving the username, its unique so its fine).
So imagine i am routing to /flower?flowerName (random route) so in this route i want to create a register and save in my database some data, but before that as i said, i should enter a middleware that checks the permission.
This is my middleware:
var jwt = require('jsonwebtoken');
var jwtConfig = require('../config/jwt');
module.exports = function(req, res, next) {
console.log("entered");
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
console.log(req.headers['x-access-token']);
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token,jwtConfig.secret, function (err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
console.log("HEREE");
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log(req.decoded);
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
my problem is, how can i get the userID for my middleware and then save it in my next route? can i pass it trough the next? like next(userID)????
How can i get the parameter then.
this is where i save the register:
var express = require('express');
var User = require('../models').User;
var Foto = require('../models').Foto;
var router = express.Router();
var jwt = require('jsonwebtoken');
var fs = require('fs');
var fsPath = require('fs-path');
module.exports = {
sendPicture: function (req, res,next) {
var bitmap = new Buffer(req.body.base64, 'base64');
var dummyDate = "25/04/14-15:54:23";
var lat = req.params.lat;
var lon = req.params.lon;
var alt = req.params.alt;
var path = __dirname + "/../public/images/" + req.params.flowerName + "/example3.png";
var fotoPath = ""
var userId = 1;
console.log(lat);
console.log(lon);
console.log(alt);
console.log(req.query.token);
fsPath.writeFile(path, bitmap, function (err) {
if (err) {
console.log(err.stack);
return err;
}
Foto.create({
image: path,
userId: userId
}).then(function () {
return res.status(200).json({ message: "foto created" });
}).catch(function(err){
console.log(err.stack);
})
});
}
}
You should be able to pass any state variables through the whole chain through res.locals, i.e.
res.locals.decoded = decoded;
next();
you can find the details on res.locals here
I've just started with meanjs. When I've taken a look at it server side module user profile controller, I find that mongoose model User is available in req object.
From where it has got added in req object?
Refer the code below, I wants to understand var user = req.user;, How user is added in req object?
'use strict';
/**
* Module dependencies.
*/
var _ = require('lodash'),
errorHandler = require('../errors.server.controller.js'),
mongoose = require('mongoose'),
passport = require('passport'),
User = mongoose.model('User');
/**
* Update user details
*/
exports.update = function(req, res) {
// Init Variables
var user = req.user;
var message = null;
// For security measurement we remove the roles from the req.body object
delete req.body.roles;
if (user) {
// Merge existing user
user = _.extend(user, req.body);
user.updated = Date.now();
user.displayName = user.firstName + ' ' + user.lastName;
user.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
} else {
res.status(400).send({
message: 'User is not signed in'
});
}
};
/**
* Send User
*/
exports.me = function(req, res) {
res.json(req.user || null);
};
In meanjs app.param([name], callback) is used, whenever a route with some id like articeId in parameter is accessed, app.param([name], callback) middleware is triggered. In meanjs it sets req.article like this.
app.param('articleId', articles.articleByID);
and in articleByID
exports.articleByID = function(req, res, next, id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(400).send({
message: 'Article is invalid'
});
}
Article.findById(id).populate('user', 'displayName').exec(function(err, article) {
if (err) return next(err);
if (!article) {
return res.status(404).send({
message: errorHandler.getErrorMessage(err)
});
}
req.article = article;
next();
});
};
more on app.param see http://expressjs.com/api.html#app.param
Passport’s authentication middleware sets req.user upon successful login.
See http://passportjs.org/guide/authenticate/.
So, I've been struggling with these for a couple of hours now. The session won't get sent to server when I use AJAX to POST something to the server, but it works fine without AJAX, like clicking links, logout, etc and this is makes me pulling my hair in frustration. Anyway, these are my codes:
var express = require('express'), // express 4
mongoskin = require('mongoskin'),
Busboy = require('busboy'),
cookieParser = require('cookie-parser'),
session = require('express-session'),
mailer = require('nodemailer'),
compress = require('compression'),
morgan = require('morgan'),
ect = require('ect'),
suspend = require('suspend'),
MongoStore = require('connect-mongo')(session);
app.use(compress());
app.engine('.ect', renderer.render);
app.set('env', 'development');
app.use(express.static(__dirname + '/public'));
app.use(cookieParser());
app.use('/admin', session({
secret : 'qlauwork secret yo',
name : 'qlauworks.sess',
proxy : true,
rolling : true,
cookie : {
maxAge : 1000 * 60 * 60 * 6
},
store : new MongoStore({
db : 'qlauworks',
auto_reconnect : true,
defaultExpirationTime : 1000 * 60 * 60 * 6
}),
unset : 'destroy'
}));
// ... etc etc
app.post('/admin/login', function (req, res) {
var msg = {};
var busboy = new Busboy({ headers : req.headers });
busboy.on('field', function (fieldName, val) {
msg[fieldName] = val;
});
busboy.on('finish', function () {
suspend.run(function * () {
msg.password = crypto.createHash('whirlpool').update(SALT).update(msg.password).digest('hex');
var user = yield db.users.findOne({ username : msg.username, password : msg.password }, suspend.resume());
if (!user) {
return res.json({ error : 'Wrong username or password' });
}
// create session token
var token = yield crypto.randomBytes(32, suspend.resume());
token = token.toString('hex');
yield db.users.update({ username : msg.username }, { $set : { token : token } }, { upsert : true }, suspend.resume());
req.session.token = token;
res.redirect('/admin/forms');
}, function (err) {
if (err) {
console.log('login: ', err);
res.send('Server error');
}
});
});
req.pipe(busboy);
});
// this is the logout and forms, works just fine
app.get('/admin/logout', auth, function (req, res) {
suspend.run(function * () {
var token = req.session.token;
yield db.users.update({ token : token }, { $unset : { token : true } }, suspend.resume());
delete req.session.token;
req.session.destroy(function (err) {
if (!err) {
res.clearCookie('qlauworks.sess', { path : '/' });
res.redirect('/admin');
}
});
}, function (err) {
if (err) {
console.log('logout: ', err);
res.json({ error : 'Server error' });
}
});
});
app.get('/admin/forms', auth, function (req, res) {
res.send(formPage);
});
// and this is the auth middleware, could logout and moving around the admin page
// but req.session always undefined if comes from an AJAX request
function auth (req, res, next) {
suspend.run(function * () {
console.log(req.session);
console.log('=====================================================')
if (!req.session.token) {
return res.json({ error : 'Invalid token' });
}
var user = yield db.users.findOne({ token : req.session.token }, suspend.resume());
if (!user.username) {
return res.json({ error : 'Invalid token' });
}
next();
}, function (err) {
if (err) {
console.log('auth: ', err);
res.json({ error : 'Server error' });
}
});
}
and this is the client side
$.post('/api/item/new', elem, function (rep) {
thisForm.find('input[type="submit"]').attr('disabled', false);
if (rep.error) {
$('#alert-multi').removeClass('success').addClass('alert').text(rep.error);
} else {
$('#alert-multi').removeClass('alert').addClass('success').text('Success');
$('input[type="reset"]').click();
for (var i = 0; i < len; i++) {
$('#preview-multi' + i).attr('src', '');
$('#multi' + i).attr('data-image-src', '');
}
}
});
So, how do I solve this?
It looks like you mounted the session middleware on /admin but you're trying to call /api/item/view.
That won't work as using express.use(path, middleware) will invoke middleware only for requests whose req.url contains path.
Either mount the session middleware on / (by not using path parameter - simple express.use(middleware) will do), or change your ajax url to start with /admin (probably not something you want to do).