jsonp GET request 404 Error - javascript

I'm using Nodejs, Socket.io, and Angular to build a web app taking advantage of the Instagram Real-Time API. I'm running into an issue when I fire off GET requests to the Instagram API.
I get this error every time I send a GET request:
"Failed to load resource: the server responded with a status of 404 (NOT FOUND)"
The error details reveal:
https://api.instagram.com/v1/geographies/[object%20Object]/media/recent?client_id=MY_CLIENT_ID
Obviously that [object%20Object] is where the error lies. As my code below shows, that should be the 'geo_id' which I'm passing in as an argument. Geo_id is actually the 'object_id' of the Instagram real-time subscription.
Below is my code. Any idea where I'm going wrong?
Socket.io server side code:
io.sockets.on('connection', function(socket) {
// log user connections
console.log("user connected");
// receive the Instagram handshake for real-time subscriptions
app.get('/callback', function(req, res){
var handshake = Instagram.subscriptions.handshake(req, res);
});
// for each new post Instagram sends us the data
app.post('/callback', function(req, res) {
var data = req.body;
// grab the object_id (as geo_id) of the subscription and send as an argument to the client side
data.forEach(function(data) {
var geo_id = data.object_id;
sendUpdate(geo_id);
});
res.end();
});
// send the url with the geo_id to the client side
// to do the ajax call
function sendUpdate(geo_id) {
io.sockets.emit('newImage', { geo_id: geo_id });
}
// log user disconnections
socket.on('disconnect', function () {
console.log('user disconnected');
});
});
relevant angular controller code:
socket.on('newImage', function(geo_id) {
// pass geo_id into Instagram API call
Instagram.get(geo_id).success(function(response) {
instagramSuccess(response.geo_id, response);
});
// Instagram API callback
var instagramSuccess = function(scope,res) {
if (res.meta.code !== 200) {
scope.error = res.meta.error_type + ' | ' + res.meta.error_message;
return;
}
if (res.data.length > 0) {
$scope.items = res.data;
} else {
scope.error = "This location has returned no results";
}
};
});
Instagram angular factory code:
angular.module('InstaFactory', []).factory('Instagram', function($http) {
var base = "https://api.instagram.com/v1";
var client_id = 'MY_CLIENT_ID';
return {
'get': function(geo_id) {
var request = '/geographies/' + geo_id + '/media/recent?client_id=' + client_id;
var url = base + request;
var config = {
'params': {
'callback': 'JSON_CALLBACK'
}
};
return $http.jsonp(url, config);
}
};
});

Related

'Cannot set headers after they are sent to the client' error received after attempted to serve new html file

I'm very new to NodeJS, and I'm trying to follow/build off of a sample project built with the Spotify API and Express. The user is prompted to authenticate on the home page, and then I want to have them land at a different html file where relevant information will be displayed from the API. To my understanding "app.get" specifies what should happen once that endpoint is navigated to, so I thought that when my client.js file gets '/nextfile', I would present it with a new html file for that endpoint with response.sendFile(__dirname + '/views/nextpage.html'); within app.get('/nextpage').
Obviously, this isn't correct, because when I run the server, it simply returns to the index.html file after authentication, with an error that reads:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I tried looking into this error, but I couldn't find anything that helped me solve my specific problem. Relevant excerpts from my client.js and server.js files are below:
Server.js
/** when home page is requested, respond with this file **/
app.get("/", function (request, response) {
response.sendFile(__dirname + '/views/index.html');
});
//-------------------------------------------------------------//
// init Spotify API wrapper
var SpotifyWebApi = require('spotify-web-api-node');
// Replace with your redirect URI, required scopes, and show_dialog preference
var redirectUri = 'http://localhost:8888/callback',
clID = '9013dc5d86b84ffca62df2f22e00968e',
clSEC = 'b9484118ab374707925b1b15100cc58b';
var scopes = ['user-top-read','streaming','user-read-private'];
var showDialog = true;
// The API object we'll use to interact with the API
var spotifyApi = new SpotifyWebApi({
clientId : clID,
clientSecret : clSEC,
redirectUri : redirectUri
});
app.get("/authorize", function (request, response) {
var authorizeURL = spotifyApi.createAuthorizeURL(scopes, null, showDialog);
console.log(authorizeURL)
response.send(authorizeURL);
});
// Exchange Authorization Code for an Access Token
app.get("/callback", function (request, response) {
var authorizationCode = request.query.code;
spotifyApi.authorizationCodeGrant(authorizationCode)
.then(function(data) {
console.log(data)
response.redirect(`/#access_token=${data.body['access_token']}&refresh_token=${data.body['refresh_token']}`)
}, function(err) {
console.log('Something went wrong when retrieving the access token!', err.message);
});
});
app.get("/logout", function (request, response) {
response.redirect('/');
});
app.get('/nextpage', function (request, response) {
**/* I want to serve his html file after the user is authenticated */**
response.sendFile(__dirname + '/views/nextpage.html');
var loggedInSpotifyApi = new SpotifyWebApi();
console.log(request.headers['authorization'].split(' ')[1]);
loggedInSpotifyApi.setAccessToken(request.headers['authorization'].split(' ')[1]);
// do stuff with the api
});
Client.js
$(function() {
$('#login').click(function() {
// Call the authorize endpoint, which will return an authorize URL, then redirect to that URL
$.get('/authorize', function(data) {
console.log(data)
window.location = data;
});
});
const hash = window.location.hash
.substring(1)
.split('&')
.reduce(function (initial, item) {
if (item) {
var parts = item.split('=');
initial[parts[0]] = decodeURIComponent(parts[1]);
}
return initial;
}, {});
window.location.hash = '';
if (hash.access_token) {
$.get({url: '/nextpage', headers: {"Authorization": `Bearer ${hash.access_token}`}}, function(data) {
// "Data" is the array of track objects we get from the API. See server.js for the function that returns it.
console.log(data)
var title = $('<h3>Your top tracks on Spotify:</h3>');
title.prependTo('#data-container-mod');
// For each of the tracks, create an element
data.items.forEach(function(track) {
var trackDiv = $('<li class="track"></li>');
trackDiv.text(track.name);
trackDiv.appendTo('#data-container ol');
});
});
}
});
Any help would be greatly appreciated.
The flow of your app in sending http headers appears to be like this from when the 'callback' url is reached at:-
A http header is sent alongside the redirect URL that contains the access token and refresh token.
Client.js takes the access token and sends it back through the /nextpage route with headers containing the access token
Send nextpage.html back to client.js and update the page accordingly..
What happens next?
Beware the browser tab still is on the route with the access tokens and refresh tokens.
So the jquery ajax request to '/nextpage' runs while the previous http headers had been sent already.
I however do not understand how the index.html is being returned..
can you try removing the window.location.hash=' ' in client.js

Send JWT from AngularJS to Node.js

An AngularJS app needs to exchange a JWT with the Node.js instance that serves it. The Node.js instance has a /user route which returns a JWT to the Angular client. What specific changes need to be made to the code below so that 1.) The AngularJS app can send the JWT back to the Node.js instance's /user route, and 2.) the Node.js code can isolate the JWT as a variable for processing?
The current AngularJS code for calling the backend /user route is:
$http.get('user').then(function(response) {
console.log('response is: ');
console.log(response);
if (response.data.token === 'anonymous') {
$rootScope.authenticated = false;
} else {
$rootScope.userJWT = response.data.token;
var payload = $rootScope.userJWT.split('.')[1];
payload = $window.atob(payload);
payload = JSON.parse(payload);
self.name = payload.name;
self.authorities = payload.authorities;
$rootScope.authenticated = true;
}
}, function() {
$rootScope.authenticated = false;
});
And the Node.js code for the backend /user route is:
app.get('/user**', function(req, res) {
console.log("You Hit The User Route TOP");
//How do we get the JWT from req?
var user = getUserName(theJwt);
var token = getToken(user);
var jwtJSON = getUser(token);
if( (jwtJSON["token"] == 'error') || jwtJSON["token"] == 'anonymous' ) {
res.sendStatus(500); // Return back that an error occurred
} else {
res.json(jwtJSON);
}
console.log("You Hit The User Route BOTTOM");
});
Note, the Node.js instance includes var jwt = require('jsonwebtoken');, and one of the processing methods will decode the JWT using var decoded = jwt.decode(token, {complete: true});, as per the jsonwebtoken API.
When using JWT there is no required way to communicate the token.
The most common way is to place the token into an HTTP Header.
On the AngularJS side you would make an HTTP request with an extra header (e.g. X-Auth-Token) which contains the JWT.
Example of AngularJS side:
var config = {
headers: {
"X-Auth-Token": $rootScope.userJWT
}
}
$http.get('routeThatNeedsJWT', config).then(function(response) { ... });
On the Node.js side you would get the contents of the header and process it using the jsonwebtoken library.
Example of Node.js side:
app.get('/routeThatNeedsJWT', function(req, res) {
var rawTokenFromHeader = req.get('X-Auth-Token'); // Get JWT from header
try {
var jwtJSON = jwt.verify(token, 'secret'); // Verify and decode JWT
res.json(jwtJSON);
} catch (err) {
res.sendStatus(500); // Return back that an error occurred
}
});
Helpful links:
Express 4.x getting header value
jsonwebtoken library verify token

How to post data from my html page to a listener?

I'm currently designing a UI for an Automated Parking System. I currently need to test if my page sends out data from a form by sending it to a listener. I currently have this code but I'm not sure why it isn't working. Any help would be greatly appreciated.
This is my code that sends the data to a local listener.
<script>
var INPARK = {cardID: $("#ticket_num").val(), lift: 1, floor: 1};
$.ajax({
type:"POST",
url: '192.168.150.148:5007',
contentType:"application/json",
data: JSON.stringify(INPARK)
});
</script>
This is the listener code.
var HOST = '192.168.150.148'; // This should be your IP of 192.168.150.XXX
var PORT = 5007;
var http = require('http');
http.createServer(function (req, res) {
// Only listen for POST requests
if (req.method === 'POST') {
var buffer = '';
req.on('data', function (chunk) {
buffer += chunk;
});
req.on('end', function () {
var path = req.url.substring(0, req.url.indexOf('/', 1)).toUpperCase();
var json;
try {
json = JSON.parse(buffer);
} catch (err) {
//
}
if (path === '/INPARK') {
// Handle INPARK request
console.log(json);
res.write('inpark results');
} else if (path === '/OUTPARK') {
// Handle OUTPARK request
console.log(json);
res.write('outpark results');
} else {
// Do nothing - Bad request
res.write('BAD REQUEST');
}
// Close the connection
res.end();
});
}
}).listen(PORT, HOST, function () {
console.log('Listening at %s:%s', HOST, PORT);
});
Your ajax request is most likely going from port 80 or 443 to 5007, which is a cross domain request, hence it will fail,
If you want to resolve this issue, read up on CORS:
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing,
and JSONP:
https://en.wikipedia.org/wiki/JSONP

io.connect: io is not defined but socket.io is loaded

I have built a node.js server that provides a client.html page with a list of messages from a mysql db. I can't make it work using an ajax call.
The client.html page is this:
<time></time>
<div id="container">Loading ...</div>
<script src="http://oclock.dyndns.org:8000/socket.io/socket.io.js"></script>
<!--<script src="socket.io/socket.io.js"></script>-->
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
// create a new websocket
var socket = io.connect('http://oclock.dyndns.org:8000');
// on message received we print all the data inside the #container div
socket.on('notification', function (data) {
var msgs = '<div>';
$.each(data.flashmsgs,function(index,flashmsg){
msgs += "<b>Messaggio inviato da " + flashmsg.created_by + "</b><br>";
msgs += flashmsg.testo;
});
msgs += '</div>';
$('#container').html(msgs);
$('time').html('Last Update:' + data.time);
});
</script>
and the code for the ajax call is the following:
(function nodeLoader(){
$.ajax({
url: "client.html",
method: "get",
data: {hk: hk },
success: function(data){
$('#messaggi').html(data);
}
});
})();
The socket.io code is loaded but I get an error on io.connect: io is not defined. Same issue if i change the url from client.html to http://oclock.dyndns.org:8000 (the url of the node.js server that is listening for requests).
Any help is appreciated!
EDIT:
server.js
var hwkey;
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
url = require('url'),
fs = require('fs'),
mysql = require('mysql'),
connectionsArray = [],
connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'flipper',
database: 'oclock',
port: 3306
}),
POLLING_INTERVAL = 3000,
pollingTimer;
// If there is an error connecting to the database
connection.connect(function(err) {
// connected! (unless `err` is set)
if (err) {
console.log(err);
}
});
// creating the server ( localhost:8000 )
app.listen(8000);
function handler(req, res) {
var origin = (req.headers.origin || "*");
if (req.method.toUpperCase() === "OPTIONS"){
res.writeHead(
"204",
"No Content",
{
"access-control-allow-origin": origin,
"access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
"access-control-allow-headers": "content-type, accept",
"access-control-max-age": 10, // Seconds.
"content-length": 0
}
);
return( res.end() );
}
console.log("INCOMING REQUEST: "+req.method+" "+req.url);
req.parsed_url = url.parse(req.url, true);
var getp = req.parsed_url.query;
hwkey = getp.hk;
fs.readFile(__dirname + '/client.html', function(err, data) {
if (err) {
console.log(err);
res.writeHead(500);
return res.end('Error loading client.html');
}
res.writeHead(
200,
{
"access-control-allow-origin": origin,
"content-length": data.length
}
);
res.end(data);
});
}
function pollingLoop(){
// Doing the database query
var query = connection.query('SELECT id, testo, created_by FROM flashmsgs WHERE hwk="'+hwkey+'" AND letto="0"'),
//var query = connection.query('SELECT max(id), testo, created_by FROM flashmsgs'),
flashmsgs = []; // this array will contain the result of our db query
// setting the query listeners
query
.on('error', function(err) {
// Handle error, and 'end' event will be emitted after this as well
console.log(err);
updateSockets(err);
})
.on('result', function(flashmsg) {
// it fills our array looping on each user row inside the db
flashmsgs.push(flashmsg);
})
.on('end', function() {
// loop on itself only if there are sockets still connected
if (connectionsArray.length) {
pollingTimer = setTimeout(pollingLoop, POLLING_INTERVAL);
updateSockets({
flashmsgs: flashmsgs
});
} else {
console.log('The server timer was stopped because there are no more socket connections on the app')
}
});
};
// creating a new websocket to keep the content updated without any AJAX request
io.sockets.on('connection', function(socket) {
console.log('Number of connections:' + connectionsArray.length);
// starting the loop only if at least there is one user connected
if (!connectionsArray.length) {
pollingLoop();
}
socket.on('disconnect', function() {
var socketIndex = connectionsArray.indexOf(socket);
console.log('socketID = %s got disconnected', socketIndex);
if (~socketIndex) {
connectionsArray.splice(socketIndex, 1);
}
});
console.log('A new socket is connected!');
connectionsArray.push(socket);
});
var updateSockets = function(data) {
// adding the time of the last update
data.time = new Date();
console.log('Pushing new data to the clients connected ( connections amount = %s ) - %s', connectionsArray.length , data.time);
console.log(hwkey);
// sending new data to all the sockets connected
connectionsArray.forEach(function(tmpSocket) {
tmpSocket.volatile.emit('notification', data);
});
};
console.log('Please use your browser to navigate to http://localhost:8000');
Okay, I misunderstood at first. I just investigated your live app and it appears your ajax call is pulling down an entire html document.
If you're loading markup via ajax and then inserting into the existing page, you don't want a full HTML document. Just send down the body content.
Also, the socket.io script reference should ideally be on the parent page, not the page loaded via ajax.

Connections are duplicationg in Socket.io

I'm developing a chat app, and the connections are duplicating.
In my route, I have:
exports.index = function (io) {
return function (req, res) {
var userId;
res.render('index', {
title: 'RandomChat.me',
test: 'test2'
});
io.sockets.on('connection', function (socket) {
userId = socket.id;
console.log("+++++++++++++++++++" + userId);
socket.emit('addUser', { userId: userId });
socket.room = 'General';
socket.join(socket.room);
socket.on('sendMessage', function (data) {
console.log(data.room);
// socket.broadcast.emit('receiveMessage', { data: data });
socket.broadcast.to(data.room).emit('receiveMessage', { message: data.message });
});
});
}
};
Client-side something like:
var socket = io.connect('http://domain.com:3000');
var userId;
socket.on('addUser', function(data) {
userId = data.userId;
console.log(userId);
});
socket.on('receiveMessage', function (data) {
console.log(data);
});
var room: "General";
var message: "Test";
socket.emit('sendMessage', { room : room, message: message });
console.log(userId + " " + message)
If I go to the app and check the console log, I see the userId, and when I reload the page, I see the same ID's twice, if I reload again, I see it 3 times and so on.
The same thing is happening in the node.js console.
So basically when connections/users are duplicated, the other users receive duplicate messages, as the sendMessage/receiveMessages functions are run more than once.
Help appreciated.
The problem is this line
io.sockets.on('connection', function (socket) {
You should not put these inside a request handler because its just wrong to add a connection handler for socket for each request. Try doing these outside the request handler and use the io object to emit/broadcast events to sockets from the request handler.
Using io.sockets.once('connection', function (data){}) instead of io.sockets.on('connection', function (data){}) fixed it. It doesn't try to recover a lost connection.

Categories