Im a beginner at using Node js. Ive developed a website with server fetching using PHP and was trying out something new. So can anyone tell me what Im doing wring here?
var mysql = require('mysql');
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
var path = require('path');
var connection = mysql.createConnection({
host : 'localhost',
user : 'username',
password: 'password',
database: 'nodelogin'
});
var app = express();
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.get('/', (request, response) => {
response.sendFile(path.join(__dirname + '/login.html'));
});
app.post('/auth', (request, response) => {
var username = request.body.username;
var password = request.body.password;
if (username && password) {
connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], (error, results, fields) => {
if (results.length > 0) {
request.session.loggedin = true;
request.session.username = username;
response.redirect('/home'); // This works
} else {
request.session.loggedin = false;
response.send('<script>alert("Incorrect Username and/or Password!")</script>');
response.redirect('/home'); // This doesnt work
}
response.end();
});
} else {
response.send('Please enter Username and Password!');
response.end();
}
});
app.get('/home', (request, response) => {
if (request.session.loggedin) {
response.send('Welcome back, ' + request.session.username + '!');
} else {
response.send('Please login to view this page!');
}
response.end();
});
app.listen(3000);
Ive put up my whole app.js but after authorizing the users login, im trying to redirect to the "/home" which works in the if case but not in the else. This is after the query in the code. The error Im getting is the following, and I really cant make heads or tails of it:
C:\xampp\htdocs\students\node_modules\mysql\lib\protocol\Parser.js:437
throw err; // Rethrow non-MySQL errors
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
←[90m at ServerResponse.setHeader (_http_outgoing.js:485:11)←[39m
at ServerResponse.header (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:771:10)
at ServerResponse.location (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:888:15)
at ServerResponse.redirect (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:926:18)
at Query.<anonymous> (C:\xampp\htdocs\students\app.js:39:26)
at Query.<anonymous> (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\Connection.js:526:10)
at Query._callback (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\Connection.js:488:16)
at Query.Sequence.end (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Sequence.js:83:24)
at Query._handleFinalResultPacket (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Query.js:149:8)
at Query.EofPacket (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Query.js:133:8) {
code: ←[32m'ERR_HTTP_HEADERS_SENT'←[39m
}
[nodemon] app crashed - waiting for file changes before starting...
Here, you attempt to use res.redirect after using res.send to send an HTML string in a response. res.send comes from the Express framework and takes in data as a parameter. res.send then will then check the structure of the parameter and set the corresponding header as well as an ETag attribute in the header. It essentially implements res.write, res.setHeaders and then finally res.end, which comes from the Nodejs core and results in a "closing" of the response, effectively rendering many of the response object methods unusable since they require the response object to be "open". res.redirect is one such method that cannot be used after res.end is called (i.e. the response has been closed/is "over"). In other frameworks, a thread is dedicated to an http response and stops when the response itself has been closed. This isn't the case in Nodejs.
I would consider sending a response to the client via res.send that the client can look for as a signal to do a redirect, perhaps using something like window.location and trigger an alert.
I found these two threads to be very helpful:
Why can I execute code after "res.send"?
What is the difference between res.end() and res.send()?
You are sending the response first and then trying to redirect,that is causing the error as the connection will be closed once response is sent.
Try to use 'next' middleware in the post callback like
app.post("auth",(req,res,next)=>{
/* Your auth logic and in the else part use next for redirection */
next('redirectionRoute')
/* Or you can keep res.redirect() and remove the res.send() in else and use appropriate route handler for the redirect route - This is better way * /
})
Related
I'm making a webapp where people can review movies, and I'm trying to make it so users can't delete other users' reviews.
Here's my AngularJS function to delete movies:
$scope.del_movie = function(movie) {
$http( {
method: 'DELETE',
url: '/movie/:title',
params: {'title': movie.title},
data: {'username': movie.username}
}).then(function successCallback(response) {
console.log(response);
return getData();
}, function errorCallback(response) {
console.log('Error: ' + response);
});
};
I've console.logged the movie.username and have received the correct username.
However, when this request gets routed to my express delete function, the req.body.username appears to be undefined. Here's that route:
app.delete("/movie/:title", requiresAuth(), function(req, res) {
paramsUsernameString = req.body.username;
oidcEmailString = JSON.stringify(req.oidc.user.email);
console.log("movie " + req.params.title);
if(paramsUsernameString != oidcEmailString){
console.log("req.params.username " + paramsUsernameString + " req.oidc.user.username " + oidcEmailString);
console.log("can't delete someone else's review!");
}
else{
Movie.findOneAndRemove(req.query, function(err, result) {
if ( err ) throw err;
res.json( {
message: "req.params.username " + paramsUsernameString + " req.oidc.user.username " + oidcEmailString,
movie: result
});
});
}
});
I've searched around, and most questions here on SO are resolved by requiring body-parser, but I've already done that:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
My POST request also uses body-parser, and that one works fine.
Appreciate any help with this, happy to provide more information if needed. Thanks!
The problem is not exactly in your code as it is in the AngularJs $http service. Link to original answer
Sending a body in an HTTP DELETE is discouraged by some providers, but the HTTP spec does not explicitly prohibit it. That's why we end up with these kinds of situations
What you should do in order to overcome this is to explicitly add a Content-Type: application/json header and force the HTTP client, or, better yet, don't send a body in a DELETE request as it is not recommended.
Instead of using the body, you should consider using path params, query params, or maybe even a user header since it seems that is what you are trying to pass in the request
Also make sure you actually use body-parser and not just require it
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json())
I am very new to Node Js and Concept of Callback Mechanism , I have working code to authenticate the User based on the LDAP using ldapjs but I wanted to know the mechanism how its working with respect to data flow and callbacks.
In the below code I have few doubts, Can someone help me clarifying
What does it means with cb(err === null, err, res);
When I do console.log with fake_res it shows as true Why its true?
I see some post referring we need use to error as first callback, Is it so?
And Finally I wanted to understand why is the res used in output and authDN are same
And finally how generally callbacks works in NodeJS
Before asking this question I have gone through many forums but couldn't relate with the below code
Link 1
Link 2
Link 3
Link 4
var express = require('express');
var util = require('util');
CircularJSON = require('circular-json');
var router = express.Router();
var ldap = require('ldapjs');
var bodyParser = require('body-parser');
var userNT;
var password;
var app = express();
function authDN(dn, password, cb, res) {
var client = ldap.createClient({
url: 'ldap://localhost:389'
});
client.bind(dn, password, function(err) {
client.unbind();
cb(err === null, err, res);
});
}
function output(fake_res, err, res) {
if (fake_res) {
console.log('success');
res.status(200).send('{"status":"success"}');
} else {
console.log('failure');
res.status(401).send('{"status":"failure"}');
}
}
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({
extended: true
})); // support encoded bodies
router.post('/login', postData);
function postData(req, res) {
userNT = req.body.ntid;
password = req.body.password;
authDN(userNT, password, output, res);
};
module.exports = router;
Okay let us go try to do it step by step :
Here if you see authDN has third parameter cb this is your callback function. Now to trace it back check the value of argument provided to this function authDN when it is called inside postData function, here cb = function output
Now first param of your output is fake_res which is either true or false, this depends on response of client.bind
If it fails you will get some error hence it will go on to be false. Here comes the answer to your question 2 because your credentials seem to be correct always this err is equal to null thus your fake_res is always true.
Answering question 4 it is because it is passed on as param to send response back to the API call you made using router.post
About number 3 it is just more readable and better to use an error first callback, but not necessary.
My code is listed below but I wanted to explain my thought process and have someone correct me at every point because I have been struggling to try and get this done the RIGHT way.
I've been struggling with this for some time(5days+) and I have not found a straight forward way to do this across the web.
So I have 2 separate node apps running. One running just express-js and another running a websocket server. I'm probably just a complete knucklehead with this, but here goes.
I setup a mongo session store. When a user successfully authenticates, the session gets created and I can re-direct the user to the logged in page. While the session lives, when the user hits the 'auth-page' I can just auto redirect the user to the 'logged in page'.
Now my understanding is, when the session gets created in the mongo-store, a cookie gets created on the web browser and it is this cookie that gets to the server for each request the page makes and express-js will nicely handle the magic internally for me and I can use something like
app.post('/', function (req, res) {
}
Where the req variable gets populated with the session id by express, because express got the cookie and decoded it.
This next part is where things are dicey and any suggestions in anyway will be a huge help.
What i'm wanting to do is, inside my app.post('/'...etc) is redirect to another page. This page loads a client which initiates a websocket connection to my websocket server and my websocket server is able to use this same session-id.
So here's the thing. My express-js http server runs as a separate process with its own port and my websocket server runs as a separate process with its own port as well. After doing enough research online, I found out many sources which indicated that, when my browser makes the connection to my websocket server it will send the cookie in the header somewhere to my websocket server. So in the browser, I have some javascript code that runs:
let clientSocket = new WebSocket("ws://socket.server.address:5005");
So then from my node websocket server, I can parse out the socket.upgradeReq.headers , get the cookie, and use that to get the session id and i'm in business. That describes what I've attempted to achieve below in my code. I have been successful doing this, however I've hit different issues when trying to parse the cookie.
Sometimes I get a single cookie & sometimes, I get multiple cookies taking the form
cookie_name1=cookie_value1;cookie_name2=cookie_value2;
cookie_name3=cookie_value3;cookie_name4=cookie_value4;
cookie_name5=cookie_value5;
Sometimes I get a single cookie & sometimes, I get multiple cookies taking the form
question 1 - why do I get multiple cookies being sent to my websocket server? Is that dictated strictly by the browser? What can I do about that if anything?
question 2 - Will the cookies ALWAYs come in that format? I would hate for the semicolon delimiter style to change and that break my code
question 3 - upon reviewing my code, my thought process can you suggest and guide me with a complete different/better implementation to achieve this? Can you suggest I change parts? My goal is to be able to spin up multiple different websocket servers & webservers and load-balance between them. I'm trying to find a reliable way to do this so that my code doesn't break... my node apps are just very frail, some direction would help. It seems like for nodejs, despite its maturity in 2017, good information lives only on stackoverflow,github issue threads and irc.freenode and I classify some of these things as basic...
packages and versions used
web-server package versions
---------------
express#4.15.2
express-session#1.15.2
mongodb#2.2.26
cookie-parser#1.4.3
body-parser#1.17.1
connect-mongodb-session#1.3.0
socket-server package versions
---------------
uws#0.14.1
below is my code
webserver.js
'use strict';
const bodyparser = require('body-parser');
const cookieparser = require('cookie-parser');
const express = require('express');
const app = express();
const express_session = require('express-session');
const connect_mongo = require('connect-mongodb-session')(express_session);
const port = process.env.NODE_WEBSERVER_PORT;
const _ = require('underscore');
const mongo_store = new connect_mongo({
uri: 'mongodb://mongo1.weave.local:27017/sessiondb',
collection: 'sess'
});
const session_time = 1000 * 60 * 5 ; // 5 minute(s)
app.use(express_session({
secret: 'superman',
cookie: {
maxAge: session_time,
httpOnly: false
},
store: mongo_store,
resave: false,
saveUninitialized: false,
name: 'inspect_the_deq',
httpOnly: false
}));
app.use(bodyparser.urlencoded({ extended: false }))
app.use(bodyparser.json());
app.set('view engine', 'pug');
app.set('views', __dirname+'/pugs')
app.use(express.static(__dirname + '/com/js'));
app.use(express.static(__dirname + '/com/asset'));
const mongo = require('mongodb').MongoClient;
const mongo_url = 'mongodb://mongo1.weave.local:27017/main';
let account = null;
let database = null;
mongo.connect(mongo_url, function(err, db) {
let collection = db.collection('account');
account = collection;
database = db;
});
app.get('/', function (req, res) {
if(req.session.user){
const user = req.session.user;
res.render('main', {message: 'user '+user+' logged in' });
console.log('session found logging you on');
}else{
res.render('login', {message: 'Login'});
console.log('no session exists');
}
});
app.post('/', function (req, res) {
const user = req.body.username, pass = req.body.password;
const seconds = session_time;
account.findOne({username: user, password: pass }, function(err, document) {
if( document !== null ){
req.session.user = user;
req.session.cookie.expires = new Date(Date.now() + seconds);
req.session.cookie.signed = true;
res.render('main', {message: 'user '+user+' logged in'});
console.log('some id is '+req.session.id);
console.log('cookie id is '+req.session.cookie);
console.log('sess id is '+req.sessionID);
}else
res.render('login', {message: 'Login', login_error: 'invalid username or password'});
});
});
app.listen(port, function () {
console.log('http server '+port);
});
Socket Server code here
'use strict';
const _ = require('underscore');
const uwsPlugin = require('uws').Server;
const socket_port = process.env.NODE_SOCKET_PORT;
const ws = new uwsPlugin({ port: socket_port, maxPayload: 0 });
//const Meepack = require('./core/meepack');
const cookieparser = require('cookie-parser');
const express_session = require('express-session');
const connect_mongo = require('connect-mongodb-session')(express_session);
const mongo_store = new connect_mongo({
uri: 'mongodb://mongo1.weave.local:27017/sessiondb',
collection: 'sess'
});
ws.on('connection', function connection(socket) {
'use strict';
console.log('client verification process ');
let headers = Object.keys(socket.upgradeReq.headers);
let upgradeReq = Object.keys(socket.upgradeReq.headers.cookie);
let cookie = socket.upgradeReq.headers.cookie;
//use the cookie here to get the session_id and do whatever you want
socket.on('close', function close(e) {
console.log('connection closed');
});
socket.on('message', function close(data) {
'use strict';
});
});
I am new to nodeJS and asynchronous programming. I am using express as the base for my app, there is really only one route that serves a page and accepts an upload from a form. I would like to make a POST request to an external service after the the file has been uploaded. Attempting to execute any code after res.send(200) however results in an error message of Error: Can't set headers after they are sent.
I am using the request package to make the post to the external service.
var express = require('express');
var router = express.Router();
var util = require("util");
var request = require("request");
/* POST uploads. */
router.post('/', function(req, res, next) {
console.log("LOG:" + util.inspect(req.files));
res.send('respond with a resource');
postFile(req.files.file.path);
});
var postFile = function(path) {
var opts = {
method: 'POST',
uri: 'https://example.com/api/files.upload',
formData: {
token: "xxx",
file: fs.createReadStream(req.files.file.path)
}
}
// console.log("LOG:\n" + util.inspect(opts));
request.post(opts, function(error, response, body) {
if (error) {
console.log("ERROR LOG: " + util.inspect(error));
} else {
console.log("RESPONSE LOG:" + util.inspect(response));
}
});
}
module.exports = router;
The postFile function works fine on it's own and even adding a console.log directly after the res.send results in the same error. How can I continue to execute code on the server after the response has been sent to the client?
Output log from node:
POST /uploads 200 85.201 ms - 23
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:154:12)
at fn (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:934:10)
at View.exports.renderFile [as engine] (/Users/jason/dev/test/file-share/node_modules/jade/lib/index.js:374:12)
at View.render (/Users/jason/dev/test/file-share/node_modules/express/lib/view.js:93:8)
at EventEmitter.app.render (/Users/jason/dev/test/file-share/node_modules/express/lib/application.js:566:10)
at ServerResponse.res.render (/Users/jason/dev/test/file-share/node_modules/express/lib/response.js:938:7)
at /Users/jason/dev/test/file-share/app.js:58:7
at Layer.handle_error (/Users/jason/dev/test/file-share/node_modules/express/lib/router/layer.js:58:5)
There's nothing wrong in the code below, you can execute code after responding the request. You just can't send headers again.
router.post('/', function(req, res, next) {
console.log("LOG:" + util.inspect(req.files));
res.send('respond with a resource');
postFile(req.files.file.path);
});
Using the request to POST or GET something will not respond to your request, unless you want to.
Your postFile() function is referring to req.files.file.path in this line:
file: fs.createReadStream(req.files.file.path)
but the req object is not being passed to it. This should generate some sort of exception.
It appears you should be using just:
file: fs.createReadStream(path)
since you are passing the path to postFile(path).
I'm trying to do a very simple Basic Auth middleware for Express on Node.js as demonstrated here: http://node-js.ru/3-writing-express-middleware
I have my middleware function:
var basicAuth = function(request, response, next) {
if (request.headers.authorization && request.headers.authorization.search('Basic ') === 0) {
// Get the username and password
var requestHeader = new Buffer(
request.headers.authorization.split(' ')[1], 'base64').toString();
requestHeader = requestHeader.split(":");
var username = requestHeader[0];
var password = requestHeader[1];
// This is an async that queries the database for the correct credentials
authenticateUser(username, password, function(authenticated) {
if (authenticated) {
next();
} else {
response.send('Authentication required', 401);
}
});
} else {
response.send('Authentication required', 401);
}
};
And I have my route:
app.get('/user/', basicAuth, function(request, response) {
response.writeHead(200);
response.end('Okay');
});
If I try to curl this request I get:
curl -X GET http://localhost/user/ --user user:password
Cannot GET /user/
This works totally cool when I add the middleware while calling createServer(), but when I do it per-request like I am in this route, it just dies quietly server-side. Unfortunately, since not all requests require authentication, I can't make this a global middleware.
I've tried flipping off Express and just using Connect and I get the same result, so I assume it's something in there. Has anybody experienced this before?
Edit: I should also mention that I've logged the relevant code exhaustively and next is being called, but it just appears to go nowhere.
Edit 2: For the record, an "empty" middleware also fails silently:
var func = function(request, response, next) {
next();
};
app.get('/user', func, function(request, response) {
response.writeHead(200);
response.end('Okay');
});
This also has the same result.
function(request, response, callback) {
vs
next();
Your supposed to either change callback to next or vica versa.
I found this link.
Express middleware: Basic HTTP Authentication
The author seems to be doing the same thing as you did, except he has a return after the next().