Node.js + Backbone collection json - javascript

I have a weird behavior in my current project. I am using Node.js in the backend and Backbone.js in the frontend. My Node.js application uses express for restful services. If I navigate to the path 127.0.0.1:999/users, everything is fine and the json data can be parsed correctly.
Actually my frontend looks like this:
//MODELS
var PersonList = Backbone.Collection.extend({
url: "http://127.0.0.1:9999/users"
});
//VIEWS
var PersonListView = Backbone.View.extend({
el : "#page",
render: function(){
var thisView = this;
var personList = new PersonList();
personList.fetch({
success: function(users, xhr){
$(thisView.el).html(users);
},
error: function(err)
{
console.log(err);
}
});
}
});
My backend looks as follows:
var express = require("express");
var app = express();
var users = [{id: 0,email: "user1#gmail.com"},
{id: 1,email: "userxx#gmail.com"},
{id: 2,email: "user123#gmail.com"}];
app.get("/users", function(request, response){
response.send(users);
});
app.listen(9999);
The fetch function does not fire the success event :(.
Any ideas what I am doing wrong?
Solved:
The problem is at the backend. Backbones request origin header is not accepted. Add the following code to your express application to get it work. In production make sure to store your allowed hosts in a separate configuration.
app.use(function(req, res, next){
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, X-Authtoken, Content-Type");
res.header("Access-Control-Allow-Methods", "HEAD, GET, POST, PUT, DELETE");
next();
});

Related

javascript / NodeJS - Sending Data from nodejs app to html file using send and get

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.

Cannot GET / DELETE Express.js

I have this script with which I'm trying to POST, GET and DELETE some stuff.
When I try POST or GET, the right messages are logged, but when I try DELETE, I get the following error:
Cannot GET /del_user
The URL I'm using is http://127.0.0.1:8081/del_user
What can be wrong in here?
var express = require('express');
var app = express();
// This responds with "Hello World" on the homepage
app.get('/', function (req, res) {
console.log("Got a GET request for the homepage");
res.send('Hello GET');
})
// This responds a POST request for the homepage
app.post('/', function (req, res) {
console.log("Got a POST request for the homepage");
res.send('Hello POST');
})
// This responds a DELETE request for the /del_user page.
app.delete('/del_user', function (req, res) {
console.log("Got a DELETE request for /del_user");
res.send('Hello DELETE');
})
// This responds a GET request for the /list_user page.
app.get('/list_user', function (req, res) {
console.log("Got a GET request for /list_user");
res.send('Page Listing');
})
// This responds a GET request for abcd, abxcd, ab123cd, and so on
app.get('/ab*cd', function(req, res) {
console.log("Got a GET request for /ab*cd");
res.send('Page Pattern Match');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
I solved it by changing the app.delete to app.get and then placing the required remove statement inside the app.get. Something like this :-
app.get('/delete/:userId', (req, res) => {
Users.remove({ _id: req.params.userId }, (error, posts) => {
if (error) {
console.warn(error);
}
else {
data = posts
res.render("delete", {"data": data})
}
});
});
In your code you're binding the /del_user URL to the HTTP DELETE method.
So all you need to do is specify the DELETE method in your application or in Postman.
If you're not using it, it's an App in Google Chrome and you might want to download it, it makes your life a LOT easier ;)
Also, since the HTTP method is already declared to be DELETE, there is no need to specify it in the URL.
This is part of the RESTful working.
If you are using AJAX to try your code, you need to specify the method, which is delete.
$.ajax({
url: "http://127.0.0.1:8081/del_user",
type: "DELETE"
});

Unable to use http-server wiki example

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();
});

Post request from Backbone client to NodeJS service

Somehow I am not able to send data from Backbone model to NodeJS service.
Backbone Model
var Money = Backbone.Model.extend({
url: 'http://localhost:3000/sendCoins',
defaults: {
fromAddress: "",
toAddress: "",
amount: ""
},
transferMoney: function(req, resp) {
//get field values
console.log(req.fromAddress); //prints fine
this.save();
}
});
var transferMoney = new Money();
Node JS service
var app = express();
app.listen(3000);
app.use(express.json());
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
app.post('/sendCoins', function(req, res) {
console.log(req.body.toAddress);
console.log(JSON.stringify(req.body));
console.log(req.body.amount);
});
When I post the request from backbone view console.log(JSON.stringify(req.body)); prints {"fromAddress":"","toAddress":"","amount":""}.
As mentionned by M Omary, you need to use the body parser in order to have access to req.body. Add the following code above app.post to see if it works:
app.use(express.bodyParser());

Using express JS for logging out a user, how do I correctly send a success response over to Backbone?

I am learing express JS and backbone.
On the express.js side I have the follwing code which logs out a user:
app.get('/logout', function(req, res) {
req.logout();
res.send('How do I correctly send a success response to the Backbone model?');
});
On the Backbone client side, I have created a model which hits: http://mysite:3000/logout url.
Here is the code of the model:
define([
'underscore',
'backbone'
], function(_, Backbone) {
var CheckLoginModel = Backbone.Model.extend({
url : 'http://localhost:3000/logout'
});
return CheckLoginModel;
});
Now, on the view, I instantiate the model and expect a success or error response:
var logoutModel = new LogoutModel(),
logoutModel.fetch({
success : function (model, xhr, options) {
// how do I use express to correctly send a success response to Backbone?
},
error : function (model, xhr, options) {
}
});
P.S.
In my express code, i could just send some dummy data and I'd be done with it... but I'm not sure that's the correct way:
app.get('/logout', function(req, res) {
req.logout();
res.send({ dummydata : 'dummydata' });
// not sure sending dummy data like this is the way.
});
Any ideas?
Ty!
Success reflects the http status 200, so you can return res.send("{}") (empty json). Also you can define the content-type to application/json in express using in your express route res.type('application/json');

Categories