JSONP support with node.js and mongo - javascript

I'm attempting to create a simple web service using node.js, express, monk, and mongodb which returns results from mongodb based on the params in the URL. I want to add jsonp support to the calls. The service will be called as such:
localhost:3000/database/collection/get?param1=Steve&param2=Frank&callback=foo
app.js
var mongo_address = 'localhost:27017/database';
var db = monk(mongo_address);
app.get('/:coll/get', routes.handle(db);
routes/index.js
exports.handle = function(db) {
return function(req, res) {
// Send request
db.get(req.params.coll).find(req.query, {fields:{_id:0}}, function(e,docs) {
if (e) throw e;
res.jsonp(docs)
});
};
}
When I use the built in JSONP support with res.jsonp, it sends the callback param to mongo and returns an empty list. I've tried stripping out the callback param during the query and then manually adding it back to the results without much luck. I feel like I'm missing something simple. Any help would be appreciated.

After some messing around with JS, I found a workable solution with minimal additional code. AFter stripping the callback from the query and storing the function value, I had to explicitly build the return string for JSONP requests.
exports.handle = function(db) {
return function(req, res) {
//Determine if URL implements JSONP
var foundCallback = req.query.callback;
var callbackVar;
//If asking for JSONP, determine the callback function name and delete it from the map
if (foundCallback){
callbackVar = req.query.callback;
delete req.query.callback
}
// Send request
db.get(req.params.coll).find(req.query, {fields:{_id:0}}, function(e,docs) {
if (e) throw e;
//If callback, send function name and query results back, else use express JSON built in
if (foundCallback)
res.send('typeof ' + callbackVar + ' === \'function\' && ' + callbackVar + '(' + JSON.stringify(docs) + ');');
else
res.json(docs);
});
};
}

Try
app.set("jsonp callback", true);

Related

Node.js how to return an array from within a asynchronous callback for use in another file

File called testing.js
I can do whatever I like with the data in saveWeatherData but cannot call this function and return the data without getting 'undefined'
For example if i tried the below code in saveWeatherData it will print out the summary as expected...
console.log(The summary of the weather today is: ${dataArray[0]});
However I want to use these values within another file such as a server file that when connected to will display weather summary temperature etc.
So I need to return an array with these values in it so that I can call this function and get my data stored in an array for further use.
I know that the reason the array --dataArray is returning undefined is because asynchronous code.
The array is returned before we have gotten the data using the callback.
My question, is there anyway to do what I am trying to do?
I tried my best to explain the problem and what I want to do, hopefully its understandable.
Would I have to use a callback inside of a callback? To callback here to return the data when its been fetched?
I just cant get my head about it and have tried multiple things to try and get the result I am looking for.
My last idea and something i would prefer not to do is the use the 'fs' module to save the data to a text or json file for use in my other files through reading the data from the saved file...
I feel im close but cant get over the last hurdle, so ive decided to ask for a little help, even just point me on the right track and Ill continue to try and figure it out.
Phew...
Thank you for your time!
const request = require("request");
let dataArray = [];
let saveWeatherData = function(weatherData) {
dataArray = weatherData;
return dataArray;
};
let getWeatherData = function(callback) {
request({
url: `https://api.forecast.io/forecast/someexamplekey/1,-1`,
json: true
}, (error, response, body) => {
//Creating array to hold weather data until we can save it using callback...
let array = [];
if (error) {
console.log("Unable to connect with Dark Sky API servers.")
}
else {
console.log(`Successfully connected to Dark Sky API servers!\n`);
array.push(body.currently.summary, body.currently.temperature, body.currently.apparentTemperature, body.currently.windSpeed, body.currently.windBearing);
callback(array);
}
});
};
getWeatherData(saveWeatherData);
module.exports = {
saveWeatherData
};
My Other File...
File called server.js
const http = require("http");
const testing = require("./testing");
function onRequest(request, response){
let data = testing.saveWeatherData();
console.log(`A user made a request: ${request.url}`);
response.writeHead(200, {"context-type": "text/plain"});
response.write("<!DOCTYPE html>");
response.write("<html>");
response.write("<head>");
response.write("<title>Weather</title>");
response.write("</head>");
response.write("<body>");
response.write("Weather summary for today: " + data[0]);
response.write("</body>");
response.write("</html>");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server is now running on port 8888...");
I'm still not sure about what are you trying to do. However, I think you're not exporting what you suppose to be exporting. To avoid the use of so many callbacks you may use async/await.
Change this part of your server.js
async function onRequest(request, response) {
let data = await testing.getWeatherData();
console.log(`A user made a request: ${request.url}`);
response.writeHead(200, { 'context-type': 'text/plain' });
response.write('<!DOCTYPE html>');
response.write('<html>');
response.write('<head>');
response.write('<title>Weather</title>');
response.write('</head>');
response.write('<body>');
response.write('Weather summary for today: ' + data[0]);
response.write('</body>');
response.write('</html>');
response.end();
}
And this of your testing.
let getWeatherData = function() {
return new Promise(resolve =>
request(
{
url: `https://api.darksky.net/forecast/someexamplekey/1,-1`,
json: true
},
(error, response, body) => {
//Creating array to hold weather data until we can save it using callback...
let array = [];
if (error) {
console.log('Unable to connect with Dark Sky API servers.');
} else {
console.log(`Successfully connected to Dark Sky API servers!\n`);
array.push(
body.currently.summary,
body.currently.temperature,
body.currently.apparentTemperature,
body.currently.windSpeed,
body.currently.windBearing
);
resolve(array);
}
}
)
);
};
module.exports = {
getWeatherData
};
It will check for new Weather in each request. If you want to save the result to avoid checking every single time you might need to do something else. But I think for a weather app the important is to keep it updated.

node express.js Can't set headers after they are sent.'

I am new to both node and express so I figure I am doing something stupid.
Complete source code can be found at:
https://github.com/wa1gon/aclogGate/tree/master/server
logRouter.get("/loggate/v1/listall", function(req, res) {
let countStr = req.param('count');
let count: number;
if (!countStr) {
count = null;
} else {
count = Number.parseInt(countStr);
if (count == NaN) count = null;
}
acConn.listAllDatabase(count, (err: string, result: Array<LogGateResp>) => {
console.log("got list all data resp")
return res.json(result).end();
});
}
);
app.use('/', logRouter);
It works the first time though, but blows up the second.
listallDatabase connects to a network socket which gets XML database back, parses it and calls back with an JS object. Which in turn calls res.json.
Suggestions?
Remove the end() after res.json().
res.josn() send the response to frontend and end() try to send the response again.
That why you are getting the error. Because node.js don't allow the API to send response twice. Either use res.end() or res.json().

Calling node controller from node controller, how to send the data?

I have a priceController node which is a lot of code called by angular in many places.
Now I have another node called frontpageController which would like to call the priceController directly, instead having to go to angular to do it.
But I cannot figure out how to feed priceController with the data the correct way (from angular its easy cause its http post).
How do I call priceController from frontpageController and send the data which it needs (propertyID)?
This is priceController
var mongoose = require('mongoose');
exports.getPrice = function(req, res) {
console.log("priceController received: " + JSON.stringify(req.body, null, 4));
console.log("propertyID: " +req.body.propertyID);
...lots of code...
res.json({error:false,priceFindResult})
console.log("Sent data back to caller");
}
And this is frontpageController
var mongoose = require('mongoose');
exports.getFrontpage = function(req, res) {
//
// Call the priceController with the PropertyID
//
var priceController = require('./priceController');
var priceModel = require('../models/priceModel');
var priceTable = mongoose.model('priceModel');
var callPriceController = function() {
return new Promise((resolve, reject) => {
console.log("=====START callPriceController=====")
priceController.getPrice(
[{ "propertyID": "WAT-606" }]
,function(err, data) {
if (!err) {
console.log("callPriceController Result: " + JSON.stringify(data, null, 4));
console.log("=====RESOLVE callPriceController=====")
resolve(data);
} else {
reject(new Error('ERR callPriceController : ' + err));
};
});
})};
The result in my node console is as follows
=====START callPriceController=====
priceController received: undefined
getFrontpage ERR: TypeError: Cannot read property 'propertyID' of undefined
So it looks like I actually call the priceController but does not manage to get the propertyID sent there.
How can I do this?
I can't tell exactly what you're going for here but the function getPrice takes the arguments req and res which are presumably supposed to be the request and response objects. When you call getPrice you're passing it an array and a function. Since the array doesn't have a property body, req.body is undefined so trying to access any property of req.body (in this case propertyId) is going to throw a TypeError. Even if you got past that point, though, it would error later when you tried to call res.json because you're passing it a function that doesn't have that method.
Maybe you could pass the req and res objects to getPrice?

Node.js respond with asynchronous data

Recently I started learning a little bit about Node.js and it's capabilities and tried to use it for some web services.
I wanted to create a web service which will serve as a proxy for web requests.
I wanted my service to work that way:
User will access my service -> http://myproxyservice.com/api/getuserinfo/tom
My service will perform request to -> http://targetsite.com/user?name=tom
Responded data would get reflected to the user.
To implement it I used the following code:
app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var proxy = require('./proxy_query.js')
function makeProxyApiRequest(name) {
return proxy.getUserData(name, parseProxyApiRequest);
}
function parseProxyApiRequest(data) {
returned_data = JSON.parse(data);
if (returned_data.error) {
console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
returned_data = '';
}
return JSON.stringify(returned_data);
}
app.post('/api/getuserinfo/tom', function(request, response) {
makeProxyApiRequest('tom', response);
//response.end(result);
});
var port = 7331;
proxy_query.js:
var https = require('https');
var callback = undefined;
var options = {
host: 'targetsite.com',
port: 443,
method: 'GET',
};
function resultHandlerCallback(result) {
var buffer = '';
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
if (callback) {
callback(buffer);
}
});
}
exports.getUserData = function(name, user_callback) {
callback = user_callback
options['path'] = user + '?name=' + name;
var request = https.get(options, resultHandlerCallback);
request.on('error', function(e){
console.log('error from proxy_query:getUserData: ' + e.message)
});
request.end();
}
app.listen(port);
I wish I didn't screwed this code because I replaced some stuff to fit my example.
Anyway, the problem is that I want to post the response to the user when the HTTP request is done and I cant find how to do so because I use express and express uses asynchronous calls and so do the http request.
I know that if I want to do so, I should pass the makeProxyApiRequest the response object so he would be able to pass it to the callback but it is not possible because of asyn problems.
any suggestions?
help will be appreciated.
As you're using your functions to process requests inside your route handling, it's better to write them as express middleware functions, taking the specific request/response pair, and making use of express's next cascade model:
function makeProxyApiRequest(req, res, next) {
var name = parseProxyApiRequest(req.name);
res.locals.userdata = proxy.getUserData(name);
next();
}
function parseProxyApiRequest(req, res, next) {
try {
// remember that JSON.parse will throw if it fails!
data = JSON.parse(res.locals.userdata);
if (data .error) {
next('An eror has occoured. details: ' + JSON.stringify(data));
}
res.locals.proxyData = data;
next();
}
catch (e) { next("could not parse user data JSON."); }
}
app.post('/api/getuserinfo/tom',
makeProxyApiRequest,
parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
Even better would be to move those middleware functions into their own file now, so you can simply do:
var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
middleware.makeProxyApiRequest,
middleware.parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
And keep your app.js as small as possible. Note that the client's browser will simply wait for a response by express, which happens once res.write, res.json or res.render etc is used. Until then the connection is simply kept open between the browser and the server, so if your middleware calls take a long time, that's fine - the browser will happily wait a long time for a response to get sent back, and will be doing other things in the mean time.
Now, in order to get the name, we can use express's parameter construct:
app.param("name", function(req, res, next, value) {
req.params.name = value;
// do something if we need to here, like verify it's a legal name, etc.
// for instance:
var isvalidname = validator.checkValidName(name);
if(!isvalidname) { return next("Username not valid"); }
next();
});
...
app.post("/api/getuserinfo/:name", ..., ..., ...);
Using this system, the :name part of any route will be treated based on the name parameter we defined using app.param. Note that we don't need to define this more than once: we can do the following and it'll all just work:
app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );
and for every route with :name, the code for the "name" parameter handler will kick in.
As for the proxy_query.js file, rewriting this to a proper module is probably safer than using individual exports:
// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
getURL: function(name, url, callback) {
request.get(url, function(err, result) {
if(err) return callback(err);
// do whatever processing you need to do to result:
var processedResult = ....
callback(false, processedResult);
});
}
};
and then we can use that as proxy = require("./lib/proxy_query"); in the middleware we need to actually do the URL data fetching.

Connect signed cookie parsing falsy

I'm having problems while trying to parse back signed cookies in express/connect application.
io.set('authorization', function (handshakeData, callback) {
if(handshakeData.headers.cookie) {
var signedCookies = cookie.parse(decodeURIComponent(handshakeData.headers.cookie));
handshakeData.cookie = connect.utils.parseSignedCookies(signedCookies, secret);
} else {
return accept('No cookie transmitted', false);
}
callback(null, true); // error first callback style
});
What happens is call to connect.utils.parseSignedCookies returns empty object. I looked into source for parse function and found out that it calls unsign method which gets a substring of encoded value and then tries to sign it again with the same secret and compare the results to verify that its the same value encoded and for some reasons it fails and values does not match. I don't know what I'm doing wrong, why those values differs and why I'm unable to get correct session ID.
My app initialization code looks like this:
app.use(express.cookieParser(secret));
app.use(express.session({
key: 'sessionID',
secret: secret,
maxAge: new Date(Date.now() + 3600000),
store: new RedisStore({
client: redisClient
})
}));
Please help and point what I'm doing wrong here. Thank you
The cookie parser is a middleware, so we have to use it like one. It will actually populate the object that you pass to it. This is how you would want to be using the parser:
// we need to use the same secret for Socket.IO and Express
var parseCookie = express.cookieParser(secret);
io.set('authorization', function(handshake, callback) {
if (handshake.headers.cookie) {
// pass a req, res, and next as if it were middleware
parseCookie(handshake, null, function(err) {
// use handshake.signedCookies, since the
// cookie parser has populated it
});
} else {
return accept('No session.', false);
}
callback(null, true);
});
The cookie parser API changed and this is what it looks like now:
module.exports = function cookieParser(secret) {
return function cookieParser(req, res, next) {
if (req.cookies) return next();
var cookies = req.headers.cookie;
req.secret = secret;
req.cookies = {};
req.signedCookies = {};
if (cookies) {
try {
req.cookies = cookie.parse(cookies);
if (secret) {
req.signedCookies = utils.parseSignedCookies(req.cookies, secret);
req.signedCookies = utils.parseJSONCookies(req.signedCookies);
}
req.cookies = utils.parseJSONCookies(req.cookies);
} catch (err) {
err.status = 400;
return next(err);
}
}
next();
};
};
So what we're doing is passing handshake as a request object, and the parser will read the headers.cookie property. Then, the cookies will be parsed, and put into req.signedCookies. Since we passed handshake as req, the cookies are now in handshake.signedCookies. Note that the cookies are only signed because you passed a secret to the parser.
I was having problems left and right with cookies/sessions/socket.io etc. It was finally #vytautas comment that helped me. In case anyone sees this, please make sure you're connecting to the correct host, whether you have it setup as localhost or an IP address or what have you. Otherwise you won't be able to parse your incoming cookies.
(Seems kind of obvious in hindsight.)

Categories