I made a web-app using AngularJs where user can upload .txt files to a server using ng-file-upload.
Now I wanted a simple Node.js server to test the upload part and watch how progress bars and error messages in the page behave, but having a very poor knowledge about how Node.js and the entire backend thing work, I tried to use the Node.js server provided by ng-file-upload's very wiki.
I tried to make some changes that brought me to this app.js file:
var http = require('http')
, util = require('util')
, multiparty = require('multiparty')
, PORT = process.env.PORT || 27372
var server = http.createServer(function(req, res) {
if (req.url === '/') {
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="title"><br>'+
'<input type="file" name="upload" multiple="multiple"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
} else if (req.url === '/upload') {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
if (err) {
res.writeHead(400, {'content-type': 'text/plain'});
res.end("invalid request: " + err.message);
return;
}
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received fields:\n\n '+util.inspect(fields));
res.write('\n\n');
res.end('received files:\n\n '+util.inspect(files));
});
} else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
});
server.listen(PORT, function() {
console.info('listening on http://127.0.0.1:'+PORT+'/');
});
and the UserController.js is simple as this
UserController = function() {};
UserController.prototype.uploadFile = function(req, res) {
// We are able to access req.files.file thanks to
// the multiparty middleware
var file = req.files.file;
console.log(file.name);
console.log(file.type);
}
module.exports = new UserController();
Inside a directive's controller in my AngularJs app I use the ng-file-upload upload service in this way
var upload = Upload.upload({
url: 'http://127.0.0.1:27372/upload',
method: 'POST',
fields: newFields,
file: newFile
}).progress(function (evt) {
$scope.progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
}).success(function (data, status, headers, config) {
console.log("OK");
}).error(function(data, status, headers, config) {
console.log("KO");
});
Finally, I start the server like so:
node app.js
and all looks fine:
listening on http://127.0.0.1:27372
With all that being said, when I launch the AngularJs web-app and try to upload a file I get the following error
OPTIONS http://127.0.0.1:27372/upload 400 (Bad Request) angular.js:10514
XMLHttpRequest cannot load http://127.0.0.1:27372/upload. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. The response had HTTP status code 400. (index):1
After some googling I found many gists used to allow CORS requests like this one, but my Node.js knowledge is so poor I don't even know where I should place those lines of code.
Furthermore, I tried to get a console.log(err) within the app.js form.parse part itself and got this printed on the terminal:
DEBUG SERVER: err =
{ [Error: missing content-type header] status: 415, statusCode: 415 }
What am I missing and what can I do to get this simple Node.js server
working?
EDIT 29/07/2015
I chosed to follow the first option suggested by #Can Guney Aksakalli, because it's the only one I can do, but even if now the code looks like this:
var server = http.createServer(function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
if (req.url === '/') {
res.writeHead(200, {'Content-type': 'text/html'});
// and the code stays the same
This solution it's not working; I keep getting the same error message in both the Chrome console and the terminal from which I called node app.js, as I wrote in the last part of my initial question.
You are serving html files on http://localhost:9000 and NodeJS application on http://localhost:27372; therefore you have CORS issue. (This issue is not related to angularjs though). You have to either enable CORS for NodeJS or serve your all application in the same domain.
Possible solutions:
1- Enabling CORS in NodeJS server
You can enable CORS in your server side by specifying allowed origins in response header. These lines would enable requests to your application from all domains. (add this to beginning of the function definition.)
var server = http.createServer(function(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
// the rest of the method ...
}
Enabling CORS for all domain is not always a good decision, please also check this.
2- Serving your html files from NodeJS application
Here with following additions you would serve your html files from NodeJS server. (You don't need to use the other server anymore.)
var serveStatic = require('serve-static');
var finalhandler = require('finalhandler');
//...
var serve = serveStatic('./path/to/your/static/folder');
var server = http.createServer(function(req, res) {
//...
var done = finalhandler(req, res);
serve(req, res, done);
});
I would also recommend you to use ExpressJS for richer server capabilities instead of vanilla node.js http server.
3- Providing a proxy connection from your html files server to nodejs app
I don't know what you are using as a server for static html files but it is possible to have a proxy between your static server to NodeJS application server.
EDIT 1
Here is a basic implementation for option 2- Serving your html files from NodeJS application.
In this example I used ExpressJS. Client side static files are served in public folder, for post request to /api/upload url would upload the file. Here is the server code app.js:
var express = require('express'),
path = require('path'),
multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty(),
PORT = process.env.PORT || 27372;
var app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.post('/api/upload', multipartyMiddleware, function(req, res) {
var file = req.files.file;
console.log(file.name);
console.log(file.type);
console.log(file.path);
});
var server = app.listen(PORT, function() {
var host = server.address().address;
var port = server.address().port;
console.log('the App listening at http://%s:%s', host, port);
});
Now public folder is served to root url. Here is the client file public/index.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Upload example</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body>
<div class="container">
<div>
<h1>Upload example</h1>
<hr />
<div ng-app="fileUpload" ng-controller="MyCtrl">
<button type="button" class="btn btn-default" ngf-select ng-model="file">Upload using model $watch</button>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="http://rawgit.com/danialfarid/ng-file-upload/master/dist/ng-file-upload.min.js"></script>
<script>
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('MyCtrl', ['$scope', 'Upload', function($scope, Upload) {
$scope.$watch('file', function() {
var file = $scope.file;
if (!file) {
return;
}
Upload.upload({
url: 'api/upload',
file: file
}).progress(function(evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
console.log('progress: ' + progressPercentage + '% ' + evt.config.file.name);
}).success(function(data, status, headers, config) {
console.log('file ' + config.file.name + 'uploaded. Response: ' + data);
}).error(function(data, status, headers, config) {
console.log('error status: ' + status);
})
});;
}]);
</script>
</body>
</html>
Now you can run node app and try it on localhost:27372 with your browser.
(Here is the gist version: https://gist.github.com/aksakalli/1a56072f066d65248885)
EDIT 2
Here is a basic implementation for option 1- Enabling CORS in NodeJS server. I am using cors package to handle header configuration, now app.js code would be like this:
var express = require('express'),
multiparty = require('connect-multiparty'),
cors = require('cors'),
multipartyMiddleware = multiparty(),
app = express(),
PORT = process.env.PORT || 27372;
app.use(cors());
app.post('/api/upload', multipartyMiddleware, function(req, res) {
var file = req.files.file;
console.log(file.name);
console.log(file.type);
console.log(file.path);
});
var server = app.listen(PORT, function() {
var host = server.address().address;
var port = server.address().port;
console.log('the App listening at http://%s:%s', host, port);
});
For the first error:
OPTIONS http://127.0.0.1:27372/upload 400 (Bad Request) angular.js:10514
The ng-file-upload Upload service which you are using removes the Content-Type header, as seen here, before the request.
But the parse method from multiparty seems to require it.
If you are working from the given example from the wiki, I would advise you to also use express and multiparty as middleware, as it is stated in that example.
Your app.js would look like that:
var express = require('express'),
// Requires multiparty
multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty();
var app = express();
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
// Example endpoint
app.post('/upload', multipartyMiddleware, function(req, res) {
// We are able to access req.files.file thanks to
// the multiparty middleware
var file = req.files.file;
console.log(file.type);
console.log(file.name);
});
app.listen(27372);
For the second error:
It's a CORS problem as mentioned. The proposed app.js should allow CORS because of the following lines:
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
Related
Simple setup question here. I have my node project which has opened a localhost server running an html page. From that html page, how can I call another script?
index.js
var http = require('http');
var fileSystem = require('fs');
var server = http.createServer(function(req, resp){
fileSystem.readFile('./main.html', function(error, fileContent){
if(error){
resp.writeHead(500, {'Content-Type': 'text/plain'});
resp.end('Error');
}
else{
resp.writeHead(200, {'Content-Type': 'text/html'});
resp.write(fileContent);
resp.end();
}
});
});
server.listen(8080);
console.log('Listening at: localhost:8080');
main.html
<!DOCTYPE html>
<html lang="en">
<body>
<p>Hello world! Node is awesome, is it not?</p>
<script src="new_script.js"></script>
</body>
</html>
This page shows the <p> text and runs on localhost:8080, then gives the error in console Uncaught SyntaxError: Unexpected token '<'
This is the new_script.js which is not running:
alert("new_script.js called");
test1 = 123; test2 = 543;
test3 = test1*test2;
alert(test3);
And my file folder structure is flat, everything is there:
So how can I get started calling more JavaScript into my loaded up html page on my node.js server?
Your current web site at localhost:8080 will do nothing but serve out a Html page with the contents of main.html, for all request paths. So the call to localhost:8080/new_script.js, the file referenced in your main page, will also be returned the contents of your main.html and not the contents of that new_script.js file. The first occurrence of the < character in your main.html probably explains that error message.
Unless you are doing this for purely academic purposes, you should be using something like Express if you want to code a real web site.
If you really want to do this with plain Node, then you have to parse and handle every url on the request object. You have to do something rather laborious such as:
var server = http.createServer(function(req, resp){
if (req.url === "/") {
fileSystem.readFile('./main.html', function(error, fileContent){
if(error){
resp.writeHead(500, {'Content-Type': 'text/plain'});
resp.end('Error');
}
else{
resp.writeHead(200, {'Content-Type': 'text/html'});
resp.write(fileContent);
resp.end();
}
});
} else if (req.url === "/new_script.js") {
fileSystem.readFile('./new_script.js', function(error, fileContent){
if(error){
resp.writeHead(500, {'Content-Type': 'text/plain'});
resp.end('Error');
}
else{
resp.writeHead(200, {'Content-Type': 'text/javascript'});
resp.write(fileContent);
resp.end();
}
});
} else {
resp.writeHead(404);
resp.end("Not found");
}
});
(Posted solution on behalf of the question author, to move it to the answer space).
This got resolved by using Express.js, which is really convenient.
npm install express --save
index.js:
const express = require('express')
const app = express()
const port = 3000
app.use(express.static("./js"));
app.get('/', (req, res) => {
//res.send('Hello World!')
res.sendFile('./main.html', { root: __dirname });
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Now all the code is the same, except I'm telling the project to use the folder "js/" to find JavaScript files. So I just moved new_script.js in there and it works :)
id like to build a sftp client app using nodejs. Currently i am able to receive lsEntries from the Server and print them to the Terminal.
var Client = require('ssh2').Client;
var express = require('express');
var app = express();
var cors = require('cors');
app.use(cors);
var connSettings = {
host: myserver,
port: 22,
username: myuser,
password: passwd
};
var remotePathToList = '/';
var conn = new Client();
conn.on('ready', function(){
conn.sftp(function(err, sftp){
if(err) throw err;
sftp.readdir(remotePathToList, function(err, list){
if(err) throw err;
var contents = []
contents = (list);
for(var i = 0; i < contents.length; i++){
console.log(contents[i].filename);
}
app.get('/data', function(req, res){
res.send(contents);
});
app.listen(3000);
//close connection
conn.end();
});
});
}).connect(connSettings);
In order to receive the data in my frontend i have the folowing JQuery Code:
$(document).ready(function(){
$("button").click(function(){
$.get('http://localhost:3000/data', {}, function(data){
var myData = (data);
console.log(myData.length);
});
});
});
But i don't receive an answer. Sometimes i get ERR_EMPTY_RESPONSE after a very long time of waiting. Note that i'm using 'cors' to prevent the Access-Control-Allow-Origin Error. However, when i type localhost:3000 in my browser i get the data printed to the screen (JSON).
What is the correct way to access the lsEntry array from the frontend?
Thanks in advance
Ah, i solved the problem. Using cors didn't work, but i found a solution, that allows cors with express:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With,
Content-Type, Accept");
next();
});
It replaces
var cors = require('cors');
app.use(cors);
and works. I don't know why the npm package didn't work, but i found the solution here.
I am using express web framework and trying to make an $http request from angularjs. I am passing data to request from client but server is not receiving request for some unknown reasons. Please help.
server.js
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io')(server);
var path = require('path');
var fs = require('fs');
app.use(express.static(path.join(__dirname, 'public')));
app.post('/readTfile',function (req,res) {
console.log('i received a request');
console.log(req.body);
});
server.listen(3000);
And angular html
<html>
<head>
<title>File tream 2</title>
<script type="text/javascript" src="javascripts/angular.js"></script>
</head>
<body>
<h2>File tream 2 AngularJS</h2>
<div ng-app = "mainApp">
<div id="readfile" ng-controller = "Ctrl1">
<div>{{myfiledata}}</div> </br></br>
</div>
</div>
</body>
<script>
var mainApp = angular.module("mainApp",[])
mainApp.controller('Ctrl1', function ($scope, $http) {
var filename = 'D:\\myapp\\public\\test.txt';
var obj = {"filename" : filename};
$scope.myfiledata = 'result';
$http({
url: '/readTfile',
method: "POST",
data: JSON.stringify(obj),
//timeout: canceller.promise,
headers: {'Content-Type': 'application/json','charset' : 'utf-8'}
}).success(function(result) {
console.log(result);
$scope.myfiledata = 'result';
}).error(function(data, status) {
console.log(data);
});
});
</script>
</html>
On console i am getting undefined for req.body
i received a request
undefined
Please help to solve me this problem.
You will need middleware to read the body of the POST request from the incoming stream and to parse it from JSON to a Javascript object and put that into req.body. It does not just end up in req.body automatically. The usual middleware for a simple JSON body would be to use the body-parser middleware that is built into Express.
// other stuff here
// read and parse application/json
app.use(express.json());
app.post('/readTfile',function (req,res) {
console.log('i received a request');
console.log(req.body);
res.send("something");
});
And, for this middleware to work and automatically recognize that you sent JSON, you will have to make sure the post has set the right content type.
Note, there is different middleware for different content types. The code above is for application/json. If you are doing a vanilla form post, that would typically have a content-type of application/x-www-form-urlencoded and you would use:
app.use(express.urlencoded());
The middleware shown here will automatically detect which content-type is available only operate on a content-type that matches their functionality so you can even have both of these middleware present.
I have a REST API server which is running on one VM1. On other VM2 machine I have built a node js server which is running as proxy. On the same VM2 machine I have application (hosted with apache which serves only html, js and css files). My node js server only resends the api calls back to the API server. This is working fine, until new requirement arrive - to add a new API endpoint (on the node js server) to download files (csv). In order to make download happen, I need to use GET method. The thing is that the required data is available only on POST endpoint from the main API server, and I am calling the API endpoint to get the data and send it back. This is the code I am trying to work it out:
var express = require('express');
var cors = require('cors');
var request = require('request');
var http = require('http');
var csv = require("fast-csv");
var config = require("./config.js");
var corsOptions = {
origin: function(origin, callback){
var originIsWhitelisted = config.whitelist.indexOf(origin) !== -1;
callback(null, originIsWhitelisted);
}
};
var handler = function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
};
var app = express();
// Enable CORS for all requests
app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // specially for pre-flight requests
app.get('/download', function(req, res){
var limit = req.query.limit;
var offset = req.query.offset;
var options = {
method: 'POST',
url: config.apiServerHost + '/search',
useQuerystring: true,
qs: {'limit': limit, 'offset': offset},
rejectUnauthorized: false,
body: 'from=date&to=date'
};
var filename = 'data.csv';
res.setHeader('Content-disposition', 'attachment; filename=\"data.csv\"');
res.setHeader('content-type', 'text/csv');
var csvStream = csv.createWriteStream({
headers: true,
objectMode: true,
transform: function (row) {
return row;
}
});
console.log(options);
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var data = JSON.parse(body);
for(var i = 0; i < data.length; i++)
csvStream.write({
"col1": "value1-"+data[0][i],
"col2": "value2-"+data[1][i],
"col3": "value3-"+data[2][i],
"col4": "value4-"+data[3][i]
});
}
csvStream.end();
}
else {
console.log("Error:", error, body);
}
}
req.pipe(request(options, callback));//.pipe(res)
csvStream.pipe(res);
});
app.use('/api', function(req, res) {
var url = config.apiServerHost + req.url;
console.log(url);
req.pipe(request({
"rejectUnauthorized": false,
"url": url
}, function(error, response, body){
if(error) {
console.log(new Date().toLocaleString(), error);
}
})).pipe(res);
});
This all code works fine when request method is POST (the same as main API server). However I receive "[Error: write after end]" when I add the body in options object. Can someone help me figure out what is happening and how to solve this problem? Thanks.
The [Error: write after end] show pip data after .end(), for your codes
req.pipe(request(options, callback));//.pipe(res)
csvStream.pipe(res);
In the callback function, the csvStream.end(); is called, then invoke csvStream.pipe could cause this error.
I'm new to Node.js.
I'm creating a simple node/express application that serves a single web page containing one button that when clicked makes a jQuery ajax request to an Express route.
The route callback makes an http.get request to openexchangerates.org for some json data containing foreign exchange rates. The JSON is then output to the Developer Tools console window.
The application works on the first button click, but on any subsequent clicks the console window displays:
GET http://127.0.0.1:3000/getFx net::ERR_CONNECTION_REFUSED
A screen grab of the Developer Tools console window shows the result of the first click, and then the second click when the connection is refused.
The error detail is as follows:
GET http://127.0.0.1:3000/getFx net::ERR_CONNECTION_REFUSED jquery-2.1.3.min.js:4
n.ajaxTransport.k.cors.a.crossDomain.send jquery-2.1.3.min.js:4
n.extend.ajax (index):18
(anonymous function) jquery-2.1.3.min.js:3
n.event.dispatch jquery-2.1.3.min.js:3
n.event.add.r.handle
My simple Node/Express application is as follows:
var express = require('express');
var app = express();
var http = require("http");
var data = "";
var json;
console.log( "__dirname", __dirname );
app.use( express.static( __dirname + '/') );
var options = {
host:"openexchangerates.org",
path:"/api/latest.json?app_id=<get free ID from openexchangerates.org>"
};
app.get("/", function( req, res ) {
res.sendFile('index.html', { root: __dirname });
})
app.get("/getfx", function(req, res) {
console.log("Route: getFx");
getFx(res);
})
function getFx(res) {
console.log("http getFx");
http.get(options, function (response) {
response.on("data", function (chunk) {
//console.log("data:\n"+chunk);
data += chunk;
});
response.on("end", function () {
json = JSON.parse(data);
console.log("http response end:");
res.end( data );
});
response.on("error", function (e) {
console.log("error:\n" + e.message);
});
})
}
app.listen(3000);
My html index page is:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Get FX</title>
<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script>
$(document).ready(function() {
console.log( "document ready");
$("#btnFx").click(function() {
console.log('clicked', this );
$.ajax({
url : "http://127.0.0.1:3000/getFx",
dataType : "json",
success : function(json) {
console.log("json returned:\n", json);
}
});
} );
})
</script>
</head>
<body>
<button id="btnFx" style="width:200px">Get foreign exchange rates</button>
</body>
For openexchangerates.org to serve the data, a free app id is required. Anyone able to help resolve this may have to go through their very short sign up:
That link is here:
https://openexchangerates.org/signup/free
However it's possible that my mistake is glowingly obvious to those with better Node/Express/jQuery knowledge.
Many thanks in advance
The way you defined your data and json vars is causing subsequent requests to fail. Since you defined them up front, all requests will re-use them, meaning by the time you JSON.parse data for the second request, data will contain two valid json strings, thus making one invalid json string. To fix this, define data and json farther down in the callback.
var express = require('express');
var app = express();
var http = require("http");
//var data = "";
//var json;
console.log( "__dirname", __dirname );
app.use( express.static( __dirname + '/') );
var options = {
host:"openexchangerates.org",
path:"/api/latest.json?app_id=<get free ID from openexchangerates.org>"
};
app.get("/", function( req, res ) {
res.sendFile('index.html', { root: __dirname });
})
app.get("/getfx", function(req, res) {
console.log("Route: getFx");
getFx(res);
})
function getFx(res) {
console.log("http getFx");
http.get(options, function (response) {
var data = "";
var json;
response.on("data", function (chunk) {
//console.log("data:\n"+chunk);
data += chunk;
});
response.on("end", function () {
console.log("http response end:");
json = JSON.parse(data);
res.json(json);
});
response.on("error", function (e) {
console.log("error:\n" + e.message);
});
})
}
app.listen(3000);
Issue comes from Cross origin requests protection which happens on localhost with chrome. Try to use other browser or just Allow origin to all hosts (*) or your host (http://localhost:3000):
app.use( express.static( __dirname + '/') );
app.use(function(req,res,next){
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
// intercept OPTIONS method
if ('OPTIONS' == req.method) {
res.send(200);
}
else {
next();
}
});