Problem receiving response from node server using ajax post request - javascript

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.

Related

Having problem with asynchronous calls in the http module

Im trying to recieve thi htttp request and, depending on the data recieved by the request, do different things. After searching around and fiddling for a while with my code i came up with this solution but it is not working and im guessing it is because of my lack of experience dealing with asynchronous code.
Heres the part of the code thats failing me. When i debug, the program doesnt even enters the req.on('data', function(chunk)) line.
const port = process.env.PORT || 1337;
server.listen(port);
const server = http.createServer((req, res) => {
var response;
var bool = false;
if(req.url == "/" ){
res.writeHead(200, {"Content-Type": "text/plain"});
res.end("Whatsupp");
}
else if (req.url == "/message"){
let datas = []
req.on('data', function(chunk){
datas.push(chunk.toString());
});
req.on('end', function(){
var challenge = JSON.parse(data).challenge;
var text = JSON.parse(data).event.text;
if(challenge != undefined){
bool = true;
response = challenge.toString();
console.log(response);
}
else if(text != undefined){
bool = true;
response = (detectType(text.toString()));
console.log(response);
}
else {
response = "algun error";
}
})
res.writeHead(bool? (200, {"Content-Type":"text/plain"}): (400, {"Content-Type":"text/plain"}) );
res.write(response);
}
}); ```

How do I read Ajax parameter value in Node without framework?

I am trying to pass a variable to my Ajax send and read it in Node without using a framework. I am not trying to return the value back to the DOM, I just need to read the value passed to Node.js. Here is what I have:
Ajax:
const XHR = new XMLHttpRequest();
XHR.open('POST', document.url, true);
XHR.setRequestHeader('X-Requested-load', 'XMLHttpRequest2');
XHR.send(`password=${password}`);
Nodejs:
const QS = require('querystring');
let password = QS.parse(req.body);
req.on('data', (data) => {
password = QS.parse(data);
});
console.log(password);
Hope this will help you:
http.createServer(function (request, response) {
if (request.method == 'POST') {
// save all data received
var postdata = '';
// receiving data
request.on('data', function(chunk) {
postdata += chunk;
// Avoid too much POST data
if (postdata.length > 1e6)
request.connection.destroy();
});
// received all data
request.on('end', function() {
var post = qs.parse(postdata);
// handle post by accessing
// post['password']
// response.send(process(post['password']));
});
} else {
console.log("Non POST request received at " + request.url);
}
}).listen();
In your example you are trying to access password out of the callback where this data is provided.
const QS = require('querystring');
let password = QS.parse(req.body);
req.on('data', (data) => {
password = QS.parse(data);
//async. access data here
console.log(password);
});

node.js redirection not working to add username

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");

How to debug failing xmlhttprequest

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().

Static HTTP File Server in NodeJs: Sometimes external .js and .css files load properly, sometimes they don't?

Edit: I know using express or whatever would be easier, but this is all a learning exercise, so sorry if this is all massively convoluted, haha!
Edit 2: It appears (after adding a few console logs for debugging) that it seems the problem has something to do with the fact that when the browser makes one request to the server (say, for style.css), it makes another request (e.g. for login-fail.js) before completing the response for the first request. It seems these multiple requests from the browser cause some sort of problem, each subsequent request preventing the previous from completing. Yikes, I need help.
Edit 3: After some debugging, it appears that the pathname variable does not change its value upon each request. For some reason, pathname's value persists over each request and this makes every request's response the same - stranger still, the value for uri changes over each request (and the uri is what gives the pathname its value...) still trying to find out why this bizarre behaviour is happening.
So I've been having this problem when the server makes requests for external .js and .css files linked to specific .html files. The responses are always inconsistent. For instance, sometimes the code will run perfectly, other times the css will load and not the js, sometimes both, sometimes neither. I am not able to determine whether this is because my code is synchronous, or for some other reason. Here is my code:
Server.js
//Module requires
var http = require("http"),
fs = require("fs"),
path = require('path'),
url = require('url'),
invoke = require("./invoke");
//Object "MIMETYPES"
//Maps relationships between file extensions and their respective MIME Content-Types
var MIMETYPES = {
".html": "text/html",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".js": "text/javascript",
".css": "text/css"
};
//Object "invokeOptions"
//Options passed to Invoke() function for POST requests
var invokeOptions = {
postData : "",
uri : ""
}
var PORT = 8888;
//HTTP Server Begin
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname;
pathname = path.resolve(__dirname, "..") + uri;
console.log("Recieved " + req.method + " request for : " + uri);
invokeOptions.uri = uri;
//GET requests wrapper
if (req.method == "GET"){
//Invoke() function handler for GET requests
if (path.extname(pathname) == ""){
invoke.invoke(invokeOptions, req, res);
return;
}
//Static file server for GET requests
fs.exists(pathname, function(exists) {
if(!exists) {
console.log("Requested file \'" + pathname + "\' doesn't exist.");
res.writeHead(404, {'Content-Type': 'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
var contentType = MIMETYPES[path.extname(pathname)];
res.writeHead(200, {"Content-Type" : contentType});
console.log("Current URI: " + uri + " has content type: " + contentType);
fs.createReadStream(pathname).pipe(res);
return;
});
}
//POST requests wrapper
if (req.method == "POST"){
var postData = "";
req.on("data", function(postPacket) {
postData += postPacket;
});
req.on("end", function() {
invokeOptions.postData = postData;
invoke.invoke(invokeOptions, req, res);
return;
});
}
}).listen(PORT);
console.log ("Server listening on port: " + PORT);
Invoke.js - This handles requests for non-files, that is requests for functions on the server
var fs = require("fs"),
querystring = require("querystring"),
path = require("path");
function invoke (options, req, res){
process.stdout.write("Invoking function --> ");
if (options.uri == "/"){
console.log("Index");
res.writeHead(200, {"Content-Type" : "text/html"});
fs.createReadStream("../index.html").pipe(res);
return;
}
if (options.uri == "/login"){
console.log("Login");
fs.readFile(path.resolve("../users.json"), "UTF-8", function(err, data){
if (err) throw err;
var json = JSON.parse(data);
var user = querystring.parse(options.postData).username,
password = querystring.parse(options.postData).password;
console.log("Submitted Username: " + user + "\nSubmitted Password: " + password);
if (json.users[0].username == user && json.users[0].password == password){
res.writeHead(200, {"Content-Type" : "text/html"});
fs.createReadStream("../app.html").pipe(res);
return;
}
else {
res.writeHead(300, {"Content-Type" : "text/html"});
fs.createReadStream("../login-fail.html").pipe(res);
return;
}
});
}
else {
console.log("Error! Bad request.");
res.writeHead(400, {"Content-Type" : "text/plain"});
res.end("Error 400: Bad Request. \nThere is no function corresponding to that request.");
}
}
exports.invoke = invoke;
Login-fail.js - This is the code that hardly ever loads
$(document).ready(function() {
var current = 3;
var countdown = $(".countdown");
function down (){
current--;
if (current != 0){
countdown.text(current);
}
else {
clearInterval(interval);
window.location.replace("./");
}
}
var interval = setInterval(down, 1000);
});
Basically, the index.html file is a form which accepts a username and password, compares the submitted POST data to a json file, and if it matches the hashes in the json file it requests app.html, otherwise it requests login-fail.html. When the login-html file is called, it has linked to it css and js which when requested hardly ever run!
Also, I thought it should be noted that the console.logs for "content-type" when requesting the css is 'text/javascript' when it doesn't work. Any help would be massively appreciated!
Holy crap.
Pathname wasn't being declared as a variable each request, because I used a ; instead of a ,
I'll go die now ladies and gents.
The relative paths you're using in your login-fail.html are probably not resolving correctly because the URL path doesn't change (/login), so the browser is looking for /login/css/style.css and /login/js/login-fail.js. Try modifying your login-fail.html to use absolute paths instead of relative paths.

Categories