GET request in javascript to NodeJS - javascript

I'm trying to do a simple conection (request - response) from the javascript code on a web to a server in Node.js.
I have tried to make the request as follows:
var request = new XMLHttpRequest();
request.open('GET', 'http://localhost:4444/', false);
request.send();
if (request.status === 200) {
console.log(request.responseText);
}
Running this code I got an error in FireBug
I have continued searching and I found that this method is only to make GET requests on the same domain. To make cross domain requests we must use other strategies.
I found a jQuery method, and it seems that i'm on the right way:
$.get(
'http://localhost:4444/',
function(data) {
alert("sucess");
//Do anything with "data"
}
);
In this case I get the same response without the error.
It seems it works but the "alert" message is never shown! What happens? What am I doing wrong?
The Node.js server code is:
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("Response");
response.end();
}).listen(4444);

So you're running into cross domain issues. You have a few options:
1) since you're using node, use socket.io . It's cross domain compliant.
On the client:
<script src="Full path to were socket IO is held on your server//socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('some_callback', function(data){
// receive data
});
socket.emit('some_other_callback', {'data': value}); //send data
</script>
Server:
var io = require('socket.io').listen(server);
// define interactions with client
io.sockets.on('connection', function(socket){
//send data to client
socket.emit('some_callback', {'data': value});
//recieve client data
socket.on('some_other_callback', function(data){
//do something
});
});
2) Since you just want to use GET you can use JSONP
$.getJSON('url_to_your_domain.com/?callback=?&other_data=something,
function(data){
//do something
}
);
Here we pass your normal GET params as well as callback=?. You will return the following from your server:
require('url');
var r = url.parse(req.url,true);
r.query.callback + '(' + some JSON + ')'
3) If you don't care about all browser compatibility you can use CORS:
You can see a much better example than I would be able to write Here

Cross domain ajax requires special support from your server.
Either CORS: http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
Which not all browsers support yet. It involves special headers in both the request and response that tell the browser that one domain is allowed to communicate with the other, and for what data.
Or JSONP: http://en.wikipedia.org/wiki/JSONP
WHich will work anywhere, but has some implementation limitations. It involves the server wrapping the response in a javascript function callback that will execute and pass in that data you want.
Either way, the server needs to be setup for each of these approaches.

I think your problem is Same Origin Policy. Your browser must get webpage from node.js instance.
Otherwise, you must use something like CORS. There also good question on SO: Ways to circumvent the same-origin policy.

Related

filter outgoing requests in node.js for logging

I am building an Express app which on certain requests has to make its own HTTP calls. I could use Superagent, request or node's own http.request.
Thing is, I need to log all of those server originating requests and their respective responses. Calling log.info before each and every of those seems silly.
How can you add a pre-filter for all outgoing HTTP calls, and ideally access both req and res?
NOTE: I am not interested in logging requests coming in to the server I am building, only in the requests that the server itself kicks off. Think of my server as a client to another black box server.
What you can do is patch http and https and proxy the request method. This way you can have a global handler that will catch the req & res objects.
var http = require('http');
var https = require('https');
var patch = function(object) {
var original = object.request;
// We proxy the request method
object.request = function(options, callback) {
// And we also proxy the callback to get res
var newCallback = function() {
var res = arguments[0];
// You can log res here
console.log("RES",res.statusCode);
callback.apply(this,arguments);
}
var req = original(options, newCallback);
// You can log your req object here.
console.log(req.method,req.path);
return req;
}
}
patch(http);
patch(https);
http.get("http://www.google.com/index.html", function(res) {
console.log("Got response");
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
Edit: This might work if you use the request npm package as well, as it might just rely on the built-in node.js http.request method anyways.
What server are you going to use for you app?
I would definally bring up such functionality on to server level. Take a look how heroku router is doing it. You can track all of needed information using some of their addons: papertrail, or newrelic ( or use them separately for you app ).
https://papertrailapp.com/
http://newrelic.com/
I like out-of-box solutions in this case, no need extend your app logic for logging such information.
If you want to have your own solution, you can setup nginx to monitor request/response info.
http://nginx.com/resources/admin-guide/logging-and-monitoring/

Angular Cross-Origin Request CORS failure, but node http.get() returns successfully

I am trying to access an API using AngularJS. I have checked the API functionality with the following node code. This rules out that the fault lies with
var http = require("http");
url = 'http://www.asterank.com/api/kepler?query={"PER":{"$lt":1.02595675,"$gt":0.67125}}&limit=10';
var request = http.get(url, function (response) {
var buffer = ""
response.on("data", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
console.log(buffer);
console.log("\n");
});
});
I run my angular app with node http-server, with the following arguments
"start": "http-server --cors -a localhost -p 8000 -c-1"
And my angular controller looks as follows
app.controller('Request', function($scope, $http){
// functional URL = http://www.w3schools.com/website/Customers_JSON.php
$scope.test = "functional";
$scope.get = function(){
$http.get('http://www.asterank.com/api/kepler?query={"PER":{"$lt":1.02595675,"$gt":0.67125}}&limit=10',{
params: {
headers: {
//'Access-Control-Allow-Origin': '*'
'Access-Control-Request-Headers' : 'access-control-allow-origin'
}
}
})
.success(function(result) {
console.log("Success", result);
$scope.result = result;
}).error(function() {
console.log("error");
});
// the above is sending a GET request rather than an OPTIONS request
};
});
The controller can parse the w3schools URL, but it consistently returns the CORS error when passed the asterank URL.
My app avails of other remedies suggested for CORS on this site (below).
Inspecting the GET requests through Firefox shows that the headers are not being added to the GET request. But beyond that I do not know how to remedy this. Help appreciated for someone learning their way through Angular.
I have tried using $http.jsonp(). The GET request executes successfully (over the network) but the angular method returns the .error() function.
var app = angular.module('sliderDemoApp', ['ngSlider', 'ngResource']);
.config(function($httpProvider) {
//Enable cross domain calls
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
});
You should understand one simple thing: even though those http modules look somewhat similar, they are totally different beasts in regards to CORS.
Actually, the node.js http.get() has nothing to do with CORS. It's your server that makes a request - in the same way as your browser does when you type this URL in its location bar and command to open it. The user agents are different, yes, but the process in general is the same: a client accesses a page lying on an external server.
Now note the difference with angular's $http.get(): a client opens a page that runs a script, and this script attempts to access a page lying on an external server. In other words, this request runs in the context of another page - lying within its own domain. And unless this domain is allowed by the external server to access it in the client code, it's just not possible - that's the point of CORS, after all.
There are different workarounds: JSONP - which basically means wrapping the response into a function call - is one possible way. But it has the same key point as, well, the other workarounds - it's the external server that should allow this form of communication. Otherwise your request for JSONP is just ignored: server sends back a regular JSON, which causes an error when trying to process it as a function call.
The bottom line: unless the external server's willing to cooperate on that matter, you won't be able to use its data in your client-side application - unless you pass this data via your server (which will act like a proxy).
Asterank now allows cross origin requests to their API. You don't need to worry about these workarounds posted above any more. A simple $http.get(http://www.asterank.com/api/kepler?query={"PER":{"$lt":1.02595675,"$gt":0.67125}}&limit=10')
will work now. No headers required.I emailed them about this issue last week and they responded and configured their server to allow all origin requests.
Exact email response from Asterank : "I just enabled CORS for Asterank (ie Access-Control-Allow-Origin *). Hope this helps!"
I was having a similar issue with CORS yesterday, I worked around it using a form, hopefully this helps.
.config(function($httpProvider){
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common = {};
$httpProvider.defaults.headers.post = {};
$httpProvider.defaults.headers.put = {};
$httpProvider.defaults.headers.patch = {};
})
.controller('FormCtrl', function ($scope, $http) {
$scope.data = {
q: "test"//,
// z: "xxx"
};
$scope.submitForm = function () {
var filters = $scope.data;
var queryString ='';
for (i in filters){
queryString=queryString + i+"=" + filters[i] + "&";
}
$http.defaults.useXDomain = true;
var getData = {
method: 'GET',
url: 'https://YOUSEARCHDOMAIN/2013-01-01/search?' + queryString,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
console.log("posting data....");
$http(getData).success(function(data, status, headers, config) {
console.log(data);
}).error(function(data, status, headers, config) {
});
}
})
<div ng-controller="FormCtrl">
<form ng-submit="submitForm()">
First names: <input type="text" name="form.firstname">
Email Address: <input type="text" ng-model="form.emailaddress">
<button>bmyutton</button>
</form>
</div>
Seems to work with the url you posted above as well..
ObjectA: 0.017DEC: 50.2413KMAG: 10.961KOI: 72.01MSTAR: 1.03PER: 0.8374903RA: 19.04529ROW: 31RPLANET: 1.38RSTAR: 1T0: 64.57439TPLANET: 1903TSTAR: 5627UPER: 0.0000015UT0: 0.00026
I should also add that in chrome you need the CORS plugin. I didn't dig into the issue quite as indepth as I should for angular. I found a base html can get around these CORS restrictions, this is just a work around until I have more time to understand the issue.
After lots of looking around. The best local solution I found for this is the npm module CORS-anywhere. Used it to create AngularJS AWS Cloudsearch Demo.

Making HTTP requests from server side

I have some code that is trying to get a JSON result from the Soundcloud API.
I registered an app, got the client id and such, and I'm trying to make a call like this:
var querystring = require('querystring');
var http = require('http');
var addr = 'http://api.soundcloud.com/resolve.json?url=http://soundcloud.com/matas/hobnotropic&client_id=XXXXX';
var options = {
hostname: "api.soundcloud.com",
path: "/resolve.json?url=http://soundcloud.com/matas/hobnotropic&client_id=XXXXXx",
method: "GET",
headers: {
"Content-Type": "application/json"
}
}
var request = http.get(options, function(response) {
response.setEncoding('utf8');
response.on('data', function(chunk) {
console.log(chunk);
});
});
This produces a result that looks like this:
{"status":"302 - Found","location":"https://api.soundcloud.com/tracks/49931.json?client_id=xxxx"}
When I use the same URL in Chrome, I get the proper JSON info. How do I properly make this call from server side script?
The built-in http client does not handle redirects. However request does and has many other features the built-in client does not support out of the box.
Today I updated my own NodeJS Api-Wrapper Package for Soundcloud, which can be found here: https://www.npmjs.com/package/soundcloud-nodejs-api-wrapper
It does server side API communication, which includes data modification. No user popup window and redirect url is needed.
I did not found yet any other package having support for this in NodeJS.

Cross-domain jQuery.getJSON from a Node.JS (using express) server does not work in Internet Explorer

This is an annoying problem, and I don't suppose that it's only IE that has this problem. Basically I have a Node.js server, from which I am making cross-domain calls to get some JSON data for display.
This needs to be a JSONP call and I give a callback in the URL. What I am not sure is, how to do this?
So the website (domainA.com) has an HTML page with a JS script like this (all works fine in Firefox 3):
<script type="text/javascript">
var jsonName = 'ABC'
var url = 'http://domainB.com:8080/stream/aires/' //The JSON data to get
jQuery.getJSON(url+jsonName, function(json){
// parse the JSON data
var data = [], header, comment = /^#/, x;
jQuery.each(json.RESULT.ROWS,function(i,tweet){ ..... }
}
......
</script>
Now my Node.js server is very simple (I'm using express):
var app = require('express').createServer();
var express = require('express');
app.listen(3000);
app.get('/stream/aires/:id', function(req, res){
request('http://'+options.host+':'+options.port+options.path, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body); // Print the google web page.
res.writeHead(200, {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'
});
res.end(JSON.stringify(JSON.parse(body)));
}
})
});
How can I change these two so they will work with cross-domain GET in IE? I have been searching the internet and there seem to be a few different things like jQuery.support.cors = true; which does not work. There also seem to be a lot of lengthy workarounds.
There is no real 'ideal' design pattern which I have been able to find for this type of thing.
Seeing as I have control over both the web page and the cross domain web service I'm sending to what is the best change to make to ensure compatability across all IE versions along with FireFox, Opera, Chrome etc?
Cheers!
Say we have two servers, myServer.com and crossDomainServer.com, both of which we control.
Assuming we want a client of myServer.com to pull some data from crossDomainServer.com, first that client needs to make a JSONP request to crossDomainServer.com:
// client-side JS from myServer.com
// script tag gets around cross-domain security issues
var script = document.createElement('script');
script.src = 'http://crossDomainServer.com/getJSONPResponse';
document.body.appendChild(script); // triggers a GET request
On the cross-domain server we need to handle this GET request:
// in the express app for crossDomainServer.com
app.get('/getJSONPResponse', function(req, res) {
res.writeHead(200, {'Content-Type': 'application/javascript'});
res.end("__parseJSONPResponse(" + JSON.stringify('some data') + ");");
});
Then in our client-side JS we need a global function to parse the JSONP response:
// gets called when cross-domain server responds
function __parseJSONPResponse(data) {
// now you have access to your data
}
Works well across a wide variety of browsers, IE 6 included.
The following code shows how to handle the GET request (using express) and how to wrap the JSON response using the callback given:
app.get('/foo', function(req, res){
res.header('Content-Type', 'application/json');
res.header('Charset', 'utf-8')
res.send(req.query.callback + '({"something": "rather", "more": "pork", "tua": "tara"});');
});

Node.js http.ServerRequest response never arrives

I'm creating a reverse HTTP proxy using Node.js for fun. The code is pretty simple at the moment. It listens on 127.0.0.1:8080 for HTTP requests and forwards these to hostname.com, responses from hostname.com are then forwarded back to the client. Nothing fancy is done yet such as rewriting redirect headers, etc. The code is as follows:
var http = require('http');
var server = http.createServer(
function(request, response) {
var proxy = http.createClient(8080, 'hostname.com')
var proxyRequest = proxy.request(request.method, request.url, request.headers);
proxyRequest.on('response', function(proxyResponse) {
proxyResponse.on('data', function(chunk) {
response.write(chunk, 'binary');
});
proxyResponse.on('end', function() {
response.end();
});
response.writeHead(proxyResponse.statusCode, proxyResponse.headers);
});
request.on('data', function(chunk) {
proxyRequest.write(chunk, 'binary');
});
request.on('end', function() {
proxyRequest.end();
});
proxyRequest.on('close', function(err) {
if (err) {
console.log('close error: ' + err + ' for ' + request.url);
}
});
});
server.listen(8080);
server.on('clientError', function(exception) {
console.log('boo a clientError occured :(');
});
All appears to work well until I browse to a page that requires many additional resources (such as images) to be fetched. Naturally the browser will generate a number of GET requests to the reverse proxy to fetch these additional resources.
When I do browse to such a page some of the http.ServerRequests for the additional resources never receive responses. If I restart the page request it almost always results in success as all the resources that were successfully fetched on the first attempt were cached (hence the browser doesn't try GET them again) and so now the browser only needs to grab a few missing ones.
At a guess I would imagine I'm hitting some kind of connection limit although I'm not sure. Any help would be greatly appreciated!
If you set up Wireshark on the proxy, you'll almost certainly see what's happening. (Note that you may need a second machine for this, because some TCP/IP stacks don't provide anything that Wireshark can listen on for loopback traffic - see this)
I'm almost certain that the problem(s) you are running into here are all down to the Connection: header - proxies MUST parse this header and handle it correctly. At a guess, I would say your code is handling the first request in a Connection: keep-alive stream and ignoring the rest. As a proxy, you are supposed to parse and remove/replace this header, and any associated headers (in this case the Keep-Alive: header), before forwarding the request to the server.
If you want to build a HTTP/1.1 proxy, it's very important that you read RFC 2616 and adhere to the many, many rules that it places on their behaviour. The particular problem you are running into here is documented in section 14.10.

Categories