I am trying to retrieve data from a database on my server to use client-side. It is failing without appearing to actually throw any errors and I can't figure out how to debug it.
My server is written in node.js/express with a MongoDB database and looks like:
var express = require('express');
var fs = require('fs');
var Sample = function() {
var self = this;
self.setupVariables = function() {
self.ipaddress = process.env.OPENSHIFT_NODEJS_IP;
self.port = process.env.OPENSHIFT_NODEJS_PORT || 8080;
if(process.env.OPENSHIFT_MONGODB_DB_PASSWORD){
connection_string = process.env.OPENSHIFT_MONGODB_DB_USERNAME + ":" +
process.env.OPENSHIFT_MONGODB_DB_PASSWORD + "#" +
process.env.OPENSHIFT_MONGODB_DB_HOST + ':' +
process.env.OPENSHIFT_MONGODB_DB_PORT + '/' +
process.env.OPENSHIFT_APP_NAME;
}
console.log(connection_string)
};
self.createRoutes = function() {
self.routes = { };
self.routes['/'] = function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.send(self.cache_get('index.html') );
};
self.routes['/testdata'] = function(req, res) {
console.log('Got request for testdata')
res.setHeader('Content-Type', 'application/json');
MongoClient.connect('mongodb://'+connection_string, function(err, db) {
if(err) throw err;
var collection = db.collection('TestData').find().limit(10).toArray(function(err, docs) {
if(err) throw err;
res.send(docs);
db.close();
})
})
}
};
self.initializeServer = function() {
self.createRoutes();
self.app = express();
['js'].forEach( function(dir) {
self.app.use('/'+dir, express.static(__dirname+'/'+dir));
})
// Add handlers for the app (from the routes).
self.app.get(function(req, res, next) {
console.log('Got request:', req); // used to debug
next();
});
for (var r in self.routes) {
self.app.get(r, self.routes[r]);
}
};
self.initialize = function() {
self.setupVariables();
self.initializeServer();
};
self.start = function() {
self.app.listen(self.port, self.ipaddress)
});
};
var myapp = new Sample();
myapp.initialize();
myapp.start();
My client side (index.html) code is:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script>
// Get data from server
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "http://my.example.com/", true);
xmlHttp.send( "/testdata" );
xmlHttp.onreadystatechange=function() {
if (xmlHttp.readyState==4 && xmlHttp.status==200) {
var data = xmlHttp.responseText
window.onload = function() {
document.getElementById('p1').innerHTML = data
}
}
}
</script>
<p id="p1"></p>
</body>
</html>
My goal is to get data from the database to use on the html page. I am trying to do that by issuing an xmlhttprequest to get /testdata from the server. Then I thought the server should route that request to the appropriate function. But when I run this (well, the version with lots of other extraneous stuff that I deleted for this post), the request just sits there with xmlHttp.status=1.
Two options for answers:
1) What is wrong?
2) How do I even go about debugging this problem? The server is hosted remotely so I don't think I can use a tool like node-inspector.
I think you are misunderstanding how to use the XHR send(). It's for actually sending data, like in a POST, PUT or DELETE. You want to actually have /testdata as part of your URL in the open() call, and use just .send().
Related
I'm trying to know how jsonp work, so I wrote a demo working on nodejs, without jQuery. But, it was not working.
Bellow was my code:
views/index.jade
doctype html
html
head
title Demo of jsonp
body
#result
script.
var xhr = new XMLHttpRequest();
var url = '/getjsonp?callback=abc'
function abc (data) {
alert('aaa')
document.getElementById('result').innerHTML = data.name;
}
xhr.open('GET', url);
xhr.send();
server.js
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var app = express();
var data = { name: 'jacket', company: 'fdd' };
app.set('views', path.join(__dirname, 'views/'));
app.set('view engine', 'jade');
app.use(bodyParser.urlencoded({ extended: false }))
app.get('/', function (req, res, next) {
res.render('index');
});
app.get('/getjsonp', function (req, res, next) {
var callback = req.query.callback;
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(callback + '(' + JSON.stringify(data) + ')');
});
app.listen(3000);
And here is the response:
abc({"name":"jacket","company":"fdd"})
As my expect, I define a method abc() in index.jade, then request '/getjsonp?callback=abc' by async ajax, it'll response a javascript which will execute the method: abc().
But it was not working like that, I dont know anywhere was wrong, hope you can tell me if you know.
Thanks!
I believe that everything is working here, it's just that in the client, you'll need to retrieve the data sent back from the server using:
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
eval(xhr.responseText);
}
};
hope this helps :)
NOTE: eval will make a string like "abc({a: 'a'})" to running the function abc with a parameter with 1 argument (which is the object {a: 'a'}
And I found another way to make it work without ajax:
<script>
function abc (data) {
alert('aaa')
document.getElementById('result').innerHTML = data.name;
}
</script>
<script type="text/javascript" src="/getjsonp?callback=abc"></script>
I am working on a Team Treehouse project that builds a dynamic website with Node.js. The user enters in a username into the search field and it displays the user's avatar, number of badges earned and the number of JavaScript points. For some reason when I enter in the user name and click search the page just goes blank. I think there might be something wrong with the 303 redirection in my router.js file. I'm still fairly new to coding so any insight would be very helpful. Here are each of my js files.
/*****app.js file******/
var router = require('./router.js');
//Problem: We need a simple way to look at a user's badge count and JavaScript points from a web browser
//Solution: Use Node.js to perform the profile look ups and serve our templates via HTTP
//Create a web server
var http = require('http');
http.createServer(function (request, response) {
router.home(request, response);
router.user(request, response);
}).listen(3000);
console.log('Server running at http://<workspace-url>');
/*****router.js file******/
var Profile = require("./profile.js");
var renderer = require('./renderer');
var querystring = require('querystring');
var commonHeader = {'Content-Type': 'text/html'};
// Handle the HTTP route GET / and POST / i.e. Home
function home(request, response) {
//if url == "/" && GET
if (request.url === '/'){
if (request.method.toLowerCase() === "get") {
//show search
console.log(request.url);
response.writeHead(200, commonHeader);
renderer.view('header', {}, response);
renderer.view('search', {}, response);
renderer.view('footer', {}, response);
response.end();
}
else {
//if url == "/" && POST
//get the post data from body
request.on('data', function(postBody){
//extract the username
var query = querystring.parse(postBody.toString());
//redirect to /:username
response.writeHead(303, {'Location': '/' + query.username });
response.end();
});
}
}
}
// Handle the HTTP route for GET /:username i.e. /chalkers
function user(request, response) {
//if url == "/...."
var username = request.url.replace('/', '');
if(user.name.length > 0){
response.writeHead(200, commonHeader);
renderer.view('header', {}, response);
//get json from Treehouse
var studentProfile = new Profile(username);
//on "end"
studentProfile.on("end", function(profileJSON){
//show profile
//Store the values which we need
var values = {
avatarUrl: profileJSON.gravatar_url,
username: profileJSON.profile_name,
badges: profileJSON.badges.length,
javascriptPoints: profileJSON.points.JavaScript
}
//Simple response
renderer.view('profile', values, response);
renderer.view('footer', {}, response);
response.end();
});
//on "error"
studentProfile.on("error", function(error){
//show error
renderer.view('error', {errorMessage: error.message}, response);
renderer.view('search', {}, response);
renderer.view('footer', {}, response);
response.end();
});
}
}
module.exports.home = home;
module.exports.user = user;
/*****profile.js file*******/
var EventEmitter = require("events").EventEmitter;
var http = require("http");
var util = require("util");
/**
* An EventEmitter to get a Treehouse students profile.
* #param username
* #constructor
*/
function Profile(username) {
EventEmitter.call(this);
profileEmitter = this;
//Connect to the API URL (http://teamtreehouse.com/username.json)
var request = http.get("http://teamtreehouse.com/" + username + ".json", function(response) {
var body = "";
if (response.statusCode !== 200) {
request.abort();
//Status Code Error
profileEmitter.emit("error", new Error("There was an error getting the profile for " + username + ". (" + http.STATUS_CODES[response.statusCode] + ")"));
}
//Read the data
response.on('data', function (chunk) {
body += chunk;
profileEmitter.emit("data", chunk);
});
response.on('end', function () {
if(response.statusCode === 200) {
try {
//Parse the data
var profile = JSON.parse(body);
profileEmitter.emit("end", profile);
} catch (error) {
profileEmitter.emit("error", error);
}
}
}).on("error", function(error){
profileEmitter.emit("error", error);
});
});
}
util.inherits( Profile, EventEmitter );
module.exports = Profile;
/*****renderer.js file*******/
var fs = require('fs');
function mergeValues(values, content) {
//Cycle over the keys
for(var key in values) {
//Replace all the {{key}} with the value from the values object
content = content.replace('{{' + key + '}}', values[key]);
}
//return merged content
return content;
}
function view(templateName, values, response) {
//Read from the template file
var fileContents = fs.readFileSync('./views/' + templateName + '.html', {encoding: 'utf8'});
//Insert values in to the content
fileContents = mergeValues(values, fileContents);
//Write out the contents to the response
response.write(fileContents);
}
module.exports.view = view;
Treehouse changed from http to https and so this example code doesn't work any longer. The reason for that is in the profile.js file. You are making calls for an http site and it doesn't exist. You need to change the code (only in profile.js) to make it connect to the https site instead.
var http = require("http");
should be changed to
var https = require("https");
and with that all references to the variable in your profile.js code should be changed to https.
As well as the hard-coded URL start:
var request = http.get("http://teamtreehous...
should be
var request = https.get("https://teamtreehous...
That should resolve the problem. Good luck!
In order to get your code in the profile.js file to run, you need to change some instances of the "http" module to "https" but, and this is important, not all instances.
What needs to remain http is the the status code error on the profile.js page. This line of code is correct:
profileEmitter.emit("error", new Error("There was an error getting the profile for " + username + ". (" + http.STATUS_CODES[response.statusCode] + ")"));
But all other instances of the http module need to change to https. For example, these lines are correct:
var url = "https://teamtreehouse.com/" + username + ".json";
var request = https.get(url, function(response){
Remember to require both modules at the top of profile.js page
var http = require("http");
var https = require("https");
I have a file called node.js:
var net = require('net');
var crypto = require('crypto');
//sjcl
var sjcl = require('./sjcl');
//retrive fb profile
var loadFb = require('./loadFb.js');
var loadFeed = require('./loadFeed.js');
//read json user file
var fs = require('fs');
var text = fs.readFileSync(__dirname + '/users','utf8');
var HOST = 'localhost';
var PORT = 7000;
net.createServer(function(sock) {
// We have a connection - a socket object
console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
// Add a 'data' event handler to this instance of socket
sock.on('data', function(data) {
console.log('User request profile of: ' + data);
//var date = (data.toString()).split("***");
//var from = date[1];
loadFb(extendetPath, function(pageData)
{
loadFeed(extendetPath2, function(pageData2)
{
var fs = require('fs');
var profileText = fs.readFileSync('/tmp/profile','utf8');
console.log(profileText);
sock.write(profileText);
});
});
});
// Add a 'close' event handler to this instance of socket
sock.on('close', function(data) {
console.log('CLOSED: ' + sock.remoteAddress +' '+ sock.remotePort);
});
}).listen(PORT);
console.log('Server listening on ' + HOST +':'+ PORT);
function returnKeyFromUser(id)
{
//text
var trovata = false;
var dati = JSON.parse(text);
for(var i=0; i<dati.friendlist.friend.length && trovata==false; i++)
{
var user = (dati.friendlist.friend[i].username).replace("\n","");
var userID = (id).replace("\n","");
if(user==userID)
{
trovata=true;
return ((dati.friendlist.friend[i].publicKey).toString()).replace("\n","");
}
}
if(trovata==false)
return null;
}
There is a small http server that receives a facebook username and what he have to do is retrieve 2 page:
a graphapi with the profile information, and a graphapi with the feed informations of a facebook profile
I copy the other two files:
var https = require('https');
module.exports = function(path, callback) {
var options = {
host: 'graph.facebook.com',
port: 443,
path: (path.toString()).replace("\n",""),
method: 'GET'
};
var req = https.get(options, function(res) {
var pageData = "";
if((path.toString()).indexOf("/")==0 && (path.toString()).indexOf("/GET /`HTTP/")!=0)
//for load only (I hope facebook profile)
{
console.log(options);
res.setEncoding('utf8');
res.on('data', function (chunk) {
pageData += chunk;
});
res.on('end', function()
{
var fs = require('fs');
fs.writeFile("/tmp/profile", pageData, function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved!");
}
});
//callback(pageData);
return;
});
}
});
};
3° file
var https = require('https');
module.exports = function(path, callback) {
var options = {
host: 'graph.facebook.com',
port: 443,
path: (path.toString()).replace("\n",""),
method: 'GET'
};
var req = https.get(options, function(res) {
var pageData = "";
if((path.toString()).indexOf("/")==0 && (path.toString()).indexOf("/GET / HTTP/")!=0) //for load only (I hope facebook profile)
{
console.log(options);
res.setEncoding('utf8');
res.on('data', function (chunk) {
pageData += chunk;
});
res.on('end', function()
{
var fs = require('fs');
fs.appendFile('/tmp/profile', "***"+pageData, function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
callback(pageData);
});
}
});
};
I don't know If there is a way to call the two file in the first file node.js but what I done is this: (to call from node.js the fist file, and from the second file call the third)
in node.js file I call the first file loadFb.js with this command:
loadFb(extendetPath, function(pageData)
{
This call saves a file on my tmp profile directory and inside I call the other file loadFeed that appends some text.
After that I have to send the entire information to the client but I have a mistake.
In order the nodejs correctly call loadFb and he write tmp - profile, than he call loadFeed
but before appending the information the node call back to the client only the half of informations that I need.
I'm not a good nodejs programmer, this is a work for my thesis.
Can someone help me?
Let's look at the following code:
res.on('end', function()
{
var fs = require('fs');
fs.appendFile('/tmp/profile', "***"+pageData, function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
callback(pageData);
});
What it does it runs the asynchronous method appendFile and immediately after that calls callback. So when the code in the callback is executed, the file is not updated yet. You need to move the callback(pageData); to the appendFile's callback. And you need to review you code keeping this in mind because I see that the same fix should be made in another file so maybe there are some similar places as well.
I have some issues with getting json data from a json file, first of all here is the error:
"NetworkError: 404 Not Found - http://localhost:8000/channels.json"
Here is the code for getting json data in my html file:
<div id="result"></div>
<script type="text/javascript">
// read episodes in channel
function ajaxRequest(){
if (window.XMLHttpRequest) // if Mozilla, Safari etc
return new XMLHttpRequest();
else
return false;
}
var mygetrequest=new ajaxRequest();
mygetrequest.onreadystatechange=function(){
if (mygetrequest.readyState==4){
if (mygetrequest.status==200 || window.location.href.indexOf("http")==-1){
var jsondata=eval("("+mygetrequest.responseText+")"); //retrieve result as an JavaScript object
var rssentries=jsondata.channels;
var output='<ul>';
for (var i=0; i<rssentries.length; i++){
output+='<li>';
output+=rssentries[i].channel_id+'</a><br>';
output+=''+rssentries[i].name+'';
output+='</li>';
}
output+='</ul>';
document.getElementById("result").innerHTML=output;
}else{
alert("An error has occured making the request");
}
}
}
mygetrequest.open("GET", "channels.json", true);
mygetrequest.send(null);
</script>
so the html works standing alone, but when i try to render it in my node server, i get error, code in my node server in express:
var express = require('express');
var http = require('http');
var app = express();
var server = module.exports = http.createServer(app);
server.listen(8000);
console.log("Express server listening on port 8000 in %s mode");
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.get('/episodes', function(req, res){
res.render('episodes.html');
});
So i have to do the json data call in the server in order to avoid the error, there's no other way?
If u are rendering on the server, you can't use Ajax. Ajax only runs from within the browser, not on the server. On the server you have to read the file directly, pass it to the renderer, and render it inside of the template. For example:
var fs = require('fs');
app.get('/episodes', function(req, res){
fs.readFile('./public/channels.json', 'utf8', function (err, data) {
var channels = JSON.parse(data);
res.render('episodes.html', channels);
});
});
Inside your ejs template you'll have to render the channels directly into the page.
Another option to go along with what Max suggested is to use res.format() to return the specific content type you want.
http://expressjs.com/api.html#res.format
res.format({
text: function(){
res.send(...);
},
html: function(){
res.send(...);
},
json: function(){
fs.readFile('./public/channels.json', 'utf8', function (err, data) {
var channels = JSON.parse(data);
res.send(channels);
});
}
});
I have written a http server using node js
var sys = require("sys"),
http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs");
http.createServer(function(request, res) {
var parsed_url = url.parse(request.url);
var uri = parsed_url.pathname;
if(uri === "/test"){
res.writeHead(200, {'Content-Type': 'text/javascript'});
request.addListener('data', function (chunk) {
var data = eval("(" + chunk + ")");
console.log(data[0].id);
})
request.addListener('end', function() {
console.log('end triggered');
res.write("Post data");
res.end();
});
}
}).listen(8080);
and i am trying to send back response of ajax request but i am unable to receive any response. Here is the code for ajax request ,
var myhttp = new XMLHttpRequest();
var url = "http://localhost:8080/test";
var data = [{"a":"1"},{"b":"2"},{"c":"3"}];
var dataJson = JSON.stringify(data);
myhttp.open('POST', url, true);
myhttp.send(dataJson);
myhttp.onreadystatechange = function() {
if ((myhttp.readyState == 4) && (myhttp.status == 200)){
alert(myhttp.responseText);
}
else if ((myhttp.readyState == 4) && (myhttp.status != 200))
{
console.log("Error in Connection");
}
Can anyone help me what i am doing wrong ...
Thanks
Vinay
Your code is almost right but on your code sample you have
console.log(data[0].id)
the data object has no property id so if you only have
console.log(data[0])
there you have a response like
{ a: '1' }
therefore you can access the property a by doing
console.log(data[0].a);
UPDATED Updated with a full example
One more thing is that you are using eval and node comes with JSON.parse bundle with it so the snippet below is how i made it work
File: app.js
var sys = require("sys"),
http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs");
http.createServer(function(request, res) {
var parsed_url = url.parse(request.url);
var uri = parsed_url.pathname;
if(uri === "/test"){
res.writeHead(200, {'Content-Type': 'text/javascript'});
request.addListener('data', function (chunk) {
// removed this - eval("(" + chunk + ")");
var data = JSON.parse(chunk);
console.log(data[0].a);
})
request.addListener('end', function() {
console.log('end triggered');
res.write("Post data");
res.end();
});
} else if(uri === "/") {
fs.readFile("./index.html",function(err, data){
if(err) throw err;
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
});
}
}).listen(8080);
On the same directory create a file index.html with the following:
<html>
<head>
<script type="text/javascript" charset="utf-8">
var myhttp = new XMLHttpRequest();
var url = "http://localhost:8080/test";
var data = [{"a":"1"},{"b":"2"},{"c":"3"}];
var dataJson = JSON.stringify(data);
myhttp.open('POST', url, true);
myhttp.send(dataJson);
myhttp.onreadystatechange = function() {
if ((myhttp.readyState == 4) && (myhttp.status == 200)){
alert(myhttp.responseText);
}
else if ((myhttp.readyState == 4) && (myhttp.status != 200))
{
console.log("Error in Connection");
}
}
</script>
</head>
<body>
</body>
</html>
That is a complete working example of what you want.
With regards to the same origin policy issues you were having is mainly due to the fact that you cant POST data between 2 different domains via ajax unless you use some tricks with iframes but that is another story.
Also i think is good for anyone to understand the backbone of a technology before moving into frameworks so fair play to you.
good luck
You have to read the data in a different way. Posted data arrives on a node server in chunks (the 'data' event), that have to be collected until the 'end' event fires. Inside this event, you are able to access your payload.
var body = '';
request.addListener('data', function (chunk) {
body += chunk;
});
request.addListener('end', function() {
console.log(body);
res.write('post data: ' + body);
});
Additionaly, there seem to be some issues with your client-side code (especially concerning the status-code checks), but i can't really help you with those as i always work with frameworks like jQuery to manage async requests.
If you want to build reliable node.js servers for web use, i highly recommend the high-performance HTTP-Framework Express. It takes away alot of the pain when developing a web-based server application in node and is maintained actively.