NodeJS - Getting error "Can't set headers after they are sent" - javascript

I am new to node and I am building rest node API.I am using express for HTTP and JWT for authentication. Whenever I request /node/me with header 'x-auth'.
It's giving error which is given below. I have set up middleware in /node/me
which query mogodb database. It finds user using header 'x-auth'
server.js:
var env = process.env.NODE_ENV || 'development';
if (env === "development") {
process.env.PORT = 3000;
process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoApp';
} else if (env === "test") {
process.env.PORT = 3000;
process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoAppTest';
}
var _ = require('lodash');
var express = require('express');
var bodyParser = require('body-parser');
var {ObjectID} = require('mongodb');
var app = express();
var {mongoose} = require('./db/mongoose');
var {Todo} = require('./models/todo');
var {User} = require('./models/user');
const {authenticate} = require('./middleware/authenticate');
const bcrypt = require('bcryptjs');
const port = process.env.PORT;
app.use(bodyParser.json());
app.get('/users/me',authenticate,(req,res) => {
res.send(req.user);
});
module.exports = {
app,
env
}
app.listen(port,() => {
console.log(`server is running ${port}`);
});
authenticate.js:
const {User} = require('./../models/user');
var authenticate = (req,res,next) => {
var token = req.header('x-auth');
User.findByToken(token).then((doc) => {
if (!doc) {
res.status(401).send('doc is not found');
}
req.token = token;
req.user = doc;
next();
}).catch((e) => {
res.status(401).send(e);
});
I am getting this error about headers
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
at ServerResponse.header (C:\Users\Yash\Desktop\node-api\node_modules\express\lib\response.js:719:10)
at ServerResponse.send (C:\Users\Yash\Desktop\node-api\node_modules\express\lib\response.js:164:12)
at app.get (C:\Users\Yash\Desktop\node-api\server\server.js:254:5)
at Layer.handle [as handle_request] (C:\Users\Yash\Desktop\node-api\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\Yash\Desktop\node-api\node_modules\express\lib\router\route.js:131:13)
at User.findByToken.then (C:\Users\Yash\Desktop\node-api\server\middleware\authenticate.js:19:2)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)

You must change to this:
if(!doc) {
res.status(401).send('doc is not found');
} else {
req.token = token;
req.user = doc;
next();
}
The reason is after the if inside code is executed the flow continues independently if you are sending a response because it is just a function call. So you are executing the error code and after that you are executing the success code, I mean the next function like if the doc exists and at the moment of the next() execution the response has been sent.
Regards,

You can only send one response with express. If you follow the series of events in your Authenticate middleware you'll discover that you are sending a response with:
res.status(401).send('doc is not found');
and then calling next(). When you call next it passes control over to the next middleware, which then sends:
res.send(req.user);
Cut the next() out of the loop when the request fails with something like:
if(!doc){
res.status(401).send('doc is not found');
return;
}

You must export authenticate. And add return
if(!doc) {
res.status(401).send('doc is not found');
return;
}
or add else
if(!doc) {
res.status(401).send('doc is not found');
} else {
req.token = token;
req.user = doc;
next();
}

Related

Request is not finished yet error with mongoDB and Express

I get a message Request is not finished yet and no data will be sent, if I do patch and delete requests in my app ( the post and get request work well)
Here are my requests
In service (front, Angular 4) I create the requests
api = 'http://localhost:3000/tasks';
deleteData(id) {
return this.http.delete( this.api, id);
}
patch(data) {
return this.http.patch( this.api, data);
}
And then call them in component
this.deleteData(this.checkedItems);
this.service.patch(result.data).subscribe(d => {
this.tasks = d;
});
The service
The PATCH request get req.body via console.log - so it should works, but it doesn't
The DELETE request doesn't get any data! The req.body is empty! But I need to pass the array of ids, so I can't do it via params.
Could you please help me or give a hint? Here is my service
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
var Schema = mongoose.Schema;
const app = express();
//Middleware for CORS
app.use(cors());
app.use(express.json());
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
var todoSchema = new Schema({
taskName: String,
createdAt: Date,
isDone: Boolean,
prioraty: String
}, {
collection: 'tasks'
});
var Model = mongoose.model('Model', todoSchema);
//replace when DB is online
mongoose.connect('mongodb://localhost:27017/admin').then(() => {
console.log("connected");
}).catch (() => {
console.log("not connected");
});
mongoose.connection.once('open', function () {
console.log('mongodb connected.');
});
app.patch('/tasks', function (req, res) {
console.log(req.body);
var updateObject = {
'taskName': req.body.taskName,
'isDone': req.body.isDone,
'prioraty': req.body.prioraty
}
var id = req.body._id;
Model.collection.update({_id : id}, {$set: updateObject});
});
app.delete('/tasks', function(req,res){
console.log('Delete', req.body);
var ids = [];
for (let i = 0; i < req.body.length; i ++) {
ids.push(req.body[i]._id);
}
var myquery = { _id: { $in: ids } };
Model.collection.deleteMany(myquery, function(err, obj) {
if (err) throw err;
});
});
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));
You need to close the connection when you're done handling the request, otherwise the client will wait for the server to send a response until the request timeout is reached.
app.patch('/tasks', function (req, res) {
...
Model.collection.update({_id : id}, {$set: updateObject}, function (err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
app.delete('/tasks', function(req,res){
...
Model.collection.deleteMany(myquery, function(err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
As for the DELETE request not having a req.body, that's because Angular 4's http client doesn't allow a body for DELETE requests. Its API for DELETE requests looks like this: this.http.delete(url, httpOptions), with no body support. You'll have to use query parameters if you need to send an array of ids. Query params does support arrays, they look something like this: https://myurl.xyz/tasks?ids[]=1&ids[]=2&ids[]=3
See https://angular.io/guide/http#url-parameters

API Route available with Any token JWT+PASSPORT

I'm building api for my app and trying to use passport jwt.
I can register new users and login(get the token back) but when I use the Postman to check the "protected" route I can use Any Token like this 'any any'
Pasport.js
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../api/models/user/userModel'),// load up the user model
config = require('../config/database'); // get db config file
module.exports = (passport)=> {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.id}, (err, user)=> {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Server.js
const express = require('express'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
morgan = require('morgan'),
passport = require('passport'),
bodyParser = require('body-parser'),
jwt = require('jsonwebtoken'),
config = require('./config/database'),
Event = require('./api/models/event/eventModel'),
User = require('./api/models/user/userModel');
mongoose.Promise = global.Promise;
mongoose.connect(config.database);
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(passport.initialize());
/* Routers registration */
const routesEvent = require('./api/routes/event/eventRoutes');
routesEvent(app);
const routesUser = require('./api/routes/user/userRoutes');
routesUser(app);
/* END Routers registration */
/* Express middleware
* which used to return more interactive messages */
app.use((req, res, next)=>{
res.status(404).send({url: req.originalUrl + ' not found'});
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
/* END Express middleware */
// Launch the server on port 3000
const server = app.listen(3000, () => {
const { address, port } = server.address();
console.log(`RESTful API server Listening at http://${address}:${port}`);
});
EventRouter.js
const jwt = require('jsonwebtoken'),
passport = require('passport');
require('../../../config/passport')(passport); // as strategy in ./passport.js needs passport object
module.exports = (app)=> {
const event = require('../../controllers/event/eventController');
// Routes
app.route('/events' )
.get( event.list_all_events, passport.authenticate('jwt', { session: false}));
};
EventController
const mongoose = require('mongoose'),
Event = mongoose.model('Events'),
getToken = require('../../../config/getToken');
exports.list_all_events = (req, res)=> {
let token = getToken(req.headers);
if(token){
Event.find({}, (err, event)=> {
if (err)
res.send(err);
res.json(event);
});
} else {
return res.status(403).send({success: false, msg: 'Unauthorized.'});
}
};
I'm definitely doing something wrong in controller or in this file
GetToken.js
module.exports = getToken = (headers)=> {
console.log(headers);
if (headers && headers.authorization) {
let parted = headers.authorization.split(' ');
//Here I can see my Token from the Postman
if (parted.length === 2) {
return parted[1];
} else {
return null;
}
} else {
return null;
}
};
Please, any ideas about a mistake that I'm making above?
Below line act as an middle ware. It is validating JWT token, once token is validated it will call JwtStrategy and setting user object in request and then it will call the actual function which is suppose to be executed.
passport.authenticate('jwt', { session: false})
You really do not need getToken function. It will be taken care by above line. If token is not validated then above line automatically returns 401.
Looks strange but when I changed code to this
EventRouter.js
app.route('/events' )
.get( passport.authenticate('jwt', { session: false}), event.list_all_events)
.post(event.create_event);
It's works like expected
The only one difference is the order in get(passport, function)

Node server can´t set headers after sent

I have a node server and an angularJS app.
I have a route to get informations about one record.
The route is '/pacientes/:id'
When I request data from that route i am getting an error.
What am I doing wrong?
//error:
Error: Can't set headers after they are sent.
at ServerResponse.setHeader (_http_outgoing.js:367:11)
at ServerResponse.header (C:\nodeapp\cloudapp\node_modules\express\lib\respo
nse.js:719:10)
at ServerResponse.send (C:\nodeapp\cloudapp\node_modules\express\lib\respons
e.js:164:12)
at ServerResponse.json (C:\nodeapp\cloudapp\node_modules\express\lib\respons
e.js:250:15)
at C:\nodeapp\cloudapp\server.js:973:10
at Array.forEach (native)
at C:\nodeapp\cloudapp\server.js:971:13
at Layer.handle [as handle_request] (C:\nodeapp\cloudapp\node_modules\expres
s\lib\router\layer.js:95:5)
at next (C:\nodeapp\cloudapp\node_modules\express\lib\router\route.js:131:13
)
at Route.dispatch (C:\nodeapp\cloudapp\node_modules\express\lib\router\route
.js:112:3)
Here is app controller:
angular.module("clinang").controller('ProcedimentosCtrl',['$scope','$http','$state',function($scope,$http,$state){
$scope.modelo={}
var tipoId=$state.params.tipoId;
if (tipoId) {
$http.get('/pacientes/' + tipoId).then(function(response){
$scope.modelo=response.data;
}, function(error){
console.log(error)
});
}
}]);
node - server.js
var express = require('express');
var bodyParser = require('body-parser');
var jwt = require('jsonwebtoken');
var expressJwt = require('express-jwt');
var path = require('path');
var app = express();
// Define the port to run on
app.set('port', process.env.port || 80);
app.use(bodyParser.json());
app.all('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
var pacientes=[
{id:1, nome:'Joao'},
{id:2, nome:'Maria'}
];
app.get('/pacientes/:id', function(req, res) {
pacientes.forEach(function (paciente) {
if (paciente.id == req.params.id) {
res.json(paciente);
return;
}
});
res.status(404).end();
});
//The 404 Route (ALWAYS Keep this as the last route)
app.get('*', function(req, res){
res.status(404).end();
});
// Listen for requests
var server = app.listen(app.get('port'), function() {
var port = server.address().port;
console.log('Magic happens on port ' + port);
});
You are trying to send multiple responses to the same request from the client, but you can only send it once.
Change this:
pacientes.forEach(function (paciente) {
if (paciente.id == req.params.id) {
res.json(paciente);
return;
}
});
To this:
var result;
pacientes.forEach(function (paciente) {
if (paciente.id == req.params.id) {
result = paciente;
}
});
if (result !== undefined) {
res.json(result);
} else {
res.sendStatus(404);
}
The functions res.json, and res.sendStatus sets the header and the response's body, and call the .end() function after that, so you don't need to do it.

Basic Auth in NodeJs doesn't work

I created the following nodejs script,which has to do with basic authentication. I run the server by typing node server,so the local url is localhost:3000. In my browser,I should be getting a prompt window where I could put the username and password. Instead of it the prompt window doesn't appear and I get a message Welcome to express.
var express = require('express');
var morgan = require('morgan');
var hostname = 'localhost';
var port = 3000;
var app = express();
app.use(morgan('dev'));
function auth (req, res, next) {
console.log(req.headers);
var authHeader = req.headers.authorization;
if (!authHeader) {
var err = new Error('You are not authenticated!');
err.status = 401;
next(err);
return;
}
var auth = new Buffer(authHeader.split(' ')[1], 'base64').toString().split(':');
var user = auth[0];
var pass = auth[1];
if (user == 'admin' && pass == 'password') {
next(); // authorized
} else {
var err = new Error('You are not authenticated!');
err.status = 401;
next(err);
}
}
app.use(auth);
app.use(express.static(__dirname + '/public'));
app.use(function(err,req,res,next) {
res.writeHead(err.status || 500, {
'WWW-Authenticate': 'Basic',
'Content-Type': 'text/plain'
});
res.end(err.message);
});
app.listen(port, hostname, function(){
console.log(`Server running at http://${hostname}:${port}/`);
});
Any ideas why is this happening?
Thanks,
Theo.

node js route not found

i have the following method to auth my users:
app.all('/*', function(req, res, next) {
// CORS headers
res.header("Access-Control-Allow-Origin", "*"); // restrict it to the required domain
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// Set custom headers for CORS
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
next();
}
});
var auth = require('./auth.js');
router.post('/login', auth.login);
app.all('/api/*', [require('./middlewares/validateRequest')]);
// If no route is matched by now, it must be a 404
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
And my Auth.js
var jwt = require('jwt-simple');
var auth = {
login: function(req, res) {
var username = req.body.username || '';
var password = req.body.password || '';
if (username == '' || password == '') {
res.status(401);
res.json({
"status": 401,
"message": "Invalid credentials"
});
return;
}
// Fire a query to your DB and check if the credentials are valid
var dbUserObj = auth.validate(username, password);
if (!dbUserObj) { // If authentication fails, we send a 401 back
res.status(401);
res.json({
"status": 401,
"message": "Invalid credentials"
});
return;
}
if (dbUserObj) {
// If authentication is success, we will generate a token
// and dispatch it to the client
res.json(genToken(dbUserObj));
}
},
validate: function(username, password) {
// spoofing the DB response for simplicity
var dbUserObj = { // spoofing a userobject from the DB.
name: 'arvind',
role: 'admin',
username: 'arvind#myapp.com'
};
return dbUserObj;
},
validateUser: function(username) {
// spoofing the DB response for simplicity
var dbUserObj = { // spoofing a userobject from the DB.
name: 'arvind',
role: 'admin',
username: 'arvind#myapp.com'
};
return dbUserObj;
}
}
// private method
function genToken(user) {
var expires = expiresIn(7); // 7 days
var token = jwt.encode({
exp: expires
}, require('../config/secret')());
return {
token: token,
expires: expires,
user: user
};
}
function expiresIn(numDays) {
var dateObj = new Date();
return dateObj.setDate(dateObj.getDate() + numDays);
}
module.exports = auth;
This server runs on port 8080.
So when i attempt to go to http://localhost:8080/login i get the following error message:
Error: Not Found
at app.use.bodyParser.urlencoded.extended (/var/www/example/backend/server.js:34:15)
at Layer.handle [as handle_request] (/var/www/example/backend/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/var/www/example/backend/node_modules/express/lib/router/index.js:302:13)
at /var/www/example/backend/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/var/www/example/backend/node_modules/express/lib/router/index.js:321:12)
at next (/var/www/example/backend/node_modules/express/lib/router/index.js:261:10)
at next (/var/www/example/backend/node_modules/express/lib/router/route.js:100:14)
at next (/var/www/example/backend/node_modules/express/lib/router/route.js:104:14)
at next (/var/www/example/backend/node_modules/express/lib/router/route.js:104:14)
at next (/var/www/example/backend/node_modules/express/lib/router/route.js:104:14)
However it seems that the rest of my auth is working because if i go to:
http://localhost:8080/api/user
I get: {"status":401,"message":"Invalid Token or Key"}
Can anyone tell me why my login does not work?
Full server script:
// BASE SETUP
// =============================================================================
var express = require('express'),
bodyParser = require('body-parser');
var app = express();
var router = express.Router();
var es = require('express-sequelize');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
// =============================================================================
//Secure
app.all('/*', function(req, res, next) {
// CORS headers
res.header("Access-Control-Allow-Origin", "*"); // restrict it to the required domain
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
// Set custom headers for CORS
res.header('Access-Control-Allow-Headers', 'Content-type,Accept,X-Access-Token,X-Key');
if (req.method == 'OPTIONS') {
res.status(200).end();
} else {
next();
}
});
var auth = require('./auth.js');
router.post('/login', auth.login);
app.all('/api/*', [require('./middlewares/validateRequest')]);
// If no route is matched by now, it must be a 404
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
var env = app.get('env') == 'development' ? 'dev' : app.get('env');
var port = process.env.PORT || 8080;
var Sequelize = require('sequelize');
// db config
var env = "dev";
var config = require('./database.json')[env];
var password = config.password ? config.password : null;
// initialize database connection
var sequelize = new Sequelize(
config.database,
config.user,
config.password,
{
logging: console.log,
define: {
timestamps: false
}
}
);
//Init models
var division_model = require('./lb_models/division/division_model')(express,sequelize,router);
var user_model = require('./lb_models/user/user_model')(express,sequelize,router);
var team_model = require('./lb_models/Team')(express,sequelize,router);
app.use('/api', router);
app.use(division_model);
app.use(user_model);
app.use(team_model);
// START THE SERVER
app.listen(port);
console.log('Magic happens on port ' + port);
Try moving your app.use(bodyParser…) statements above the login route. The order of middleware matters. At the time login is called the req object hasn't run through the bodyParser middleware yet.
Also, your router instance is mounted at "/api" so the router methods will never get called for "/login". The following line should be place above your 404 catchall:
app.use('/', router);
Before, you had used app.use('/api', router), which means that your router routes will only be looked at for any request that starts with '/api'. Also, you had place the 'use' statement too far down.
When setting up middleware, the order in which you call app.use() is key. In your server.js, you're setting up your application routes before you set up body parser. Meaning, when the request comes in, is is not parsed before hitting your application logic. You need to move the app.use(bodyParser) parts to the top of your code.
var express = require('express'),
bodyParser = require('body-parser');
var app = express();
var router = express.Router();
var es = require('express-sequelize');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
perphaps you have to move the
app.use("/", (req, res, next) => {
res.status("404").json({message: "Not found"})
})
to the bottom of your code, but before "app.listen()", The order you declare the routes in the router are important, so putting the "app.use" after you declare all theses routes, would search a match with all the previous route and if none is found then it will enter in that last one
Like this:
.
..
...
app.use('/api', router);
app.use(division_model);
app.use(user_model);
app.use(team_model);
app.use("/", (req, res, next) => {
res.status("404").json({message: "Not found"})
})
// START THE SERVER
app.listen(port);
console.log('Magic happens on port ' + port);

Categories