Object method in Express-Session - javascript

Currently I'm trying to link an object with an express session.
There is my code :
var express = require('express');
var session = require('express-session');
// I have an object named "engine", which is a fake SQL Object connection (for example)
// this is my engineFactory (which return an engine when function "giveMeObject" is called).
var engineFactory = require('./tools/engineFactory');
var eF = new engineFactory();
// create my app
var port = 3030;
var app = express();
app.use(session({
secret: "secret"
}));
app.get('/', function(req, res) {
// Req.session.engine will contains an SQL connection
req.session.engine = eF.giveMeObject();
res.send('object gived : ' + req.session.engine); // return "object gived : [object Object]", so ok.
});
app.get('/session', function(req, res) {
// Here I verify if my engine still exists
res.send("Coming From Session: " + req.session.engine); // return "Coming From Session: [object Object]" so, ok.
});
app.get('/session-test', function(req, res) {
// Here I
res.send(Object.getOwnPropertyNames(req.session.engine)); // return ["attributeA","attributeB"], so where is my connectMe() ?
req.session.engine.connectMe(); // error : "req.session.engine.connectMe is not a function"
});
app.listen(port);
console.log('app listen to ' + port);
So, my problem is, I wanna link an object to a session (typically a SQL connection object). And re-use this object "everywhere" to execute queries, etc.
But when I try to use my function I have the following error message :
"req.session.engine.connectMe is not a function"
Just for information, my engine object, and the engine factory code :
Engine
function engine(){
this.attributeA = "aaa";
this.attributeB = "bbb";
};
engine.prototype.connectMe = function(){
return this.attributeA + this.attributeB;
};
module.exports = engine;
EngineFactory
var engine = require('./engine');
function engineFactory() {
};
engineFactory.prototype.giveMeObject = function() {
return new engine;
};
module.exports = engineFactory;
As I said, the goal is to link a SQL connection with a user session. The connection is gived to the user, then, the app re-use the user's connection to ask queries to the database (about that, I know that the pool connection pattenr is better, but this is a requirement of this project for many reasons).
But currently I can't re-use the object's method...
Thanks for the help.

Most backing session stores cannot/do not serialize complex types like functions. Many stores will simply call JSON.stringify() on the session data and store that as-is, which will either implicitly remove functions and other complex types or it will convert them to some other type such as a plain object or a string (depending on the availability of .toJSON()/.toString() on the objects).
You will need to re-create the engine instance to have access to functions and other non-serializable types.

Related

Cannot get objects' values from request body while trying to POST [Node.js, MySQL]

I am working on a management system, currently creating the POST requests for the api. I need the values of the request's body to post a new city in the database. The values are used in the stored procedure as parameters. Instead of the key's values which I entered, I am getting an "undefined" value, sometimes a "[object Object]".
I am using a MySQL server, hosted in the cloud by Google's services. Backend is made with Node.js, routing with Express. None of my attempts to fix the bug worked for me.
What I've tried so far:
-Parsing each key's value .toString() and JSON.stingfify() from the body
-Converting the body to Json/string, then back to a javascript object
-Getting the response's requests's body (res.req.body)
-Getting the body's response values in an array, then pushing the next element after it has been passed as a parameter to the stored procedure
-Using the 'request' npm extension to put the values I need when calling the POST method.
-Changed the values to be stored in the URL' parameters instead of the body.
-Sent the body's values as form-data, JSON file, HTML page
Controller method in cityController.js:
exports.city_post = (req, res, next)=>{
poolDb.getConnection(function (err, connection){
if(!err) {
const sql = 'CALL createNewCity(?,?)';
var zipReq = req.params.zip;
var nameReq = req.params.name;
var reqBody = JSON.stringify(req.res.body);
connection.query(sql,[zipReq,nameReq], (err,rows)=>{
if(!err){
return res.status(201).json({Message: 'City with name: '+nameReq+' and zip code: '+zipReq+' created successfully\n', rows});
}
else{
return res.status(404).json({errorMessage: err})
}
});
}
else{
return res.status(500).json({errorMessage: "server error: "+this.err});
}
console.log("\nZip Code: "+ zipReq +"\nName: " + nameReq); //for testing
console.log("\nrequest body: " + reqBody); //for testing
});
}
City route:
const express = require('express');
const router = express.Router();
const CityController = require('../controllers/cityController.js');
router.get('/', CityController.city_getAll);
router.get('/:cityzip', CityController.city_getbyzip);
router.post('/add', CityController.city_post);
...
module.exports = router;
Expected: Posting a new field in table city, status code (201).
Actual: Status code (404), no new insertion in the DB. body, req.body.zip & req.body.name are of value "undefined".
Screenshots:
-Terminal output: https://imgur.com/a/brqKZlP
-Postman request: https://imgur.com/a/ZfFcX8Z
Express doesn't parse post body by default (for some reason). You can try popular body-parser npm package, or collect the raw body data and parse from a string yourself if you don't want to add a whole new package. Here as express app:
app.use(function(req, res, next){
var data = "";
req.on('data', function(chunk){ data += chunk})
req.on('end', function(){
req.rawBody = data;
var json = JSON.parse(req.rawBody); // assuming valid json. ideally check content-type first
next();
});
});

Cannot read property 'insert' of undefined when trying to put data in MongoDB

I'm getting this error: "Cannot read property 'insert' of undefined" when trying to insert data into a database. The error shows on:
db.coordinates.insert({ "x" : "data.x", "y" : "data.y"})
Database name - "node5"
Collection name - "coordinates"
// Including libraries
var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
var static = require('node-static'); // for serving files
//db connection
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/node5');
var url = 'mongodb://localhost:27017/node5';
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected correctly to server.");
db.close();
});
// This will make all the files in the current folder
// accessible from the web
var fileServer = new static.Server('./');
// This is the port for our web server.
// you will need to go to http://localhost:8080 to see it
app.listen(8080);
// If the URL of the socket server is opened in a browser
function handler(request, response) {
request.addListener('end', function () {
fileServer.serve(request, response);
}).resume();
}
// Delete this row if you want to see debug messages
io.set('log level', 1);
// Listen for incoming connections from clients
io.sockets.on('connection', function (socket) {
// Listen for mouse move events
socket.on('post', function (data) {
console.log('posted');
console.log(data);
socket.broadcast.emit('posted', data); // Broadcasts event to everyone except originating client
db.coordinates.insert({ "x" : "data.x", "y" : "data.y"})
});
});
When writing an answer, please note that I'm new to node.js and I might not understand if you tell the answer in a complex way:)
If you are using monk for your project, then you can drop the mongodb module, since it's functionality is being wrapped up by monk. From Monk's documentation, you should be doing something like:
const monk = require('monk');
const db = monk('localhost:27017/node5')
const coordinates = db.get('coordinates');
Now that you have a reference to your coordinates collection, you can use it later in your code:
coordinates.insert({ x: data.x, y: data.y });
I hope this is easy enough to understand. If it is still confusing, then please comment below and I'll elaborate further :)

Parsing cookies with socket.io

I am trying to properly read cookies on my node server that were set by me through the browser console on localhost:3000 like this:
document.cookie = "tagname = test;secure";
document.cookie = "hello=1"
In my node server, I use sockets.io, and when I get a connection request, I can access a property which goes like this:
socket.request.headers.cookie
It's a string, and I always see it like this:
'io=QhsIVwS0zIGd-OliAAAA' //what comes after io= is random.
I've tried to translate it with various modules but they can't seem to parse the string. this is my latest attempt:
var cookie = require('cookie');
io.sockets.on('connection', function(socket) {
socket.on('addUser', function(){
var a = socket.request.headers.cookie;
var b = cookie.parse(a); //does not translate
console.log(b);
});
}
I obviously want to get an object with all the cookies that were sent by each io.connect on the browser.
I've been trying to solve it for 5 hours and I really don't know what I am doing wrong here.
Use the Cookie module. It is exactly what you are looking for.
var cookie = require('cookie');
cookie.parse(str, options)
Parse an HTTP Cookie header string and returning an object of all cookie name-value pairs. The str argument is the string representing a Cookie header value and options is an optional object containing additional parsing options.
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
// { foo: 'bar', equation: 'E=mc^2' }
Hope this helps
Without Regexp
//Get property directly without parsing
function getCookie(cookie, name){
cookie = ";"+cookie;
cookie = cookie.split("; ").join(";");
cookie = cookie.split(" =").join("=");
cookie = cookie.split(";"+name+"=");
if(cookie.length<2){
return null;
}
else{
return decodeURIComponent(cookie[1].split(";")[0]);
}
}
//getCookie('foo=bar; equation=E%3Dmc%5E2', 'equation');
//Return : "E=mc^2"
Or if you want to parse the cookie to object
//Convert cookie string to object
function parseCookie(cookie){
cookie = cookie.split("; ").join(";");
cookie = cookie.split(" =").join("=");
cookie = cookie.split(";");
var object = {};
for(var i=0; i<cookie.length; i++){
cookie[i] = cookie[i].split('=');
object[cookie[i][0]] = decodeURIComponent(cookie[i][1]);
}
return object;
}
//parseCookie('tagname = test;secure');
//Return : {tagname: " test", secure: "undefined"}
Try using socket.handshake instead of socket.request
The IO cookie is the default cookie socket.io uses as a user id. You can set this but if you don't it will create one and set a hash value to it.
Read about the option here.
I don't think it is a code issue. Here is an example of your code. When I added the cookie test and set it to 1
var app = require('express')();
var http = require('http').Server(app);
var cookie = require('cookie')
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.sockets.on('connection', function(socket) {
socket.on('chat message', function(){
var a = socket.request.headers.cookie;
var b = cookie.parse(a); //does not translate
console.log(b);
});
});
http.listen(port, function(){
console.log('listening on *:' + port);
});
Server Output
{ io: 'TxvLfvIupubZpOaGAAAF', test: '1' }
If I changed the it to this.
var io = require('socket.io')(http, {
cookie : 'id'
});
The output would change this to.
{ id: 'ZJPSwFsQAje0SrgsAAAD', test: '1' }

Send messages from server to client socket.io

I am trying to send a message from NodeJS server to client using socket.io
However, I found the same practice all over the internet, which is wrapping the emit with io.on('connection', handler) and then making the server listen on a special "channel" event like so:
var io = require('socket.io')();
var socketioJwt = require('socketio-jwt');
var jwtSecret = require('./settings').jwtSecret;
var User = require('./models/users').User;
io.set('authorization', socketioJwt.authorize({
secret: jwtSecret,
handshake: true
}));
var sockets = [];
io.on('connection', function(socket) {
sockets.push(socket);
});
sendLiveUpdates = function(gameSession) {
console.log(sockets);
}
exports.sendLiveUpdates = sendLiveUpdates;
exports.io = io;
My problem is: I want to emit messages outside this on connection wrapper, example from my routes or other scripts. Is it possible?
Thanks.
Yes. You just need to keep a reference to the socket.
// Just an array for sockets... use whatever method you want to reference them
var sockets = [];
io.on('connection', function(socket) {
socket.on('event', function() {
io.emit('another_event', message);
});
// Add the new socket to the array, for messing with later
sockets.push(socket);
});
Then somewhere else in your code...
sockets[0].emit('someEvent');
What I usually do is assign new clients a UUID and add them to an object keyed by this UUID. This comes in handy for logging and what not as well, so I keep a consistent ID everywhere.

How do I add temporary properties on a mongoose object just for response, which is not stored in database

I would like to fill a couple of extra temporary properties with additional data and send back to the response
'use strict';
var mongoose = require('mongoose');
var express = require('express');
var app = express();
var TournamentSchema = new mongoose.Schema({
createdAt: { type: Date, default: Date.now },
deadlineAt: { type: Date }
});
var Tournament = mongoose.model('Tournament', TournamentSchema);
app.get('/', function(req, res) {
var tournament = new Tournament();
// Adding properties like this 'on-the-fly' doesnt seem to work
// How can I do this ?
tournament['friends'] = ['Friend1, Friend2'];
tournament.state = 'NOOB';
tournament.score = 5;
console.log(tournament);
res.send(tournament);
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
But the properties wont get added on the Tournament object and therefor not in the response.
Found the answer here: Unable to add properties to js object
I cant add properties on a Mongoose object, I have to convert it to plain JSON-object using the .toJSON() or .toObject() methods.
EDIT: And like #Zlatko mentions, you can also finalize your queries using the .lean() method.
mongooseModel.find().lean().exec()
... which also produces native js objects.

Categories

Resources