Can't send POST from angular.js to express.js - node.js

I having problems to communicate between my angular.js APP and my express.js REST.
I'm using yeoman 1.0 with generator-angular 0.7.1.
I tried to use a middleware config for my grunt serve but i did not get it working.
Angular App (port: 9000):
angular.module('wboxApp')
.controller('AdminCtrl', function ($scope, $routeParams, $http, fbRef) {
var ref = fbRef();
var token = $routeParams.token;
$http.post('http://127.0.0.1:3000/box/token/get', {token: token}).success(function (data) {
console.log(data);
}
});
});
Express API (port: 3000):
app.post('/box/token/get', function (req, res) {
res.header('Access-Control-Allow-Origin', req.headers.origin || "*");
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,HEAD,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'content-Type,x-requested-with');
var token = req.body.token;
var tokenRef = ref.child('tokens').child(token);
tokenRef.once('value', function (data) {
var fullToken = data.val();
fullToken = fullToken + '.' + token;
if (data.val()) {
res.json({fullToken: fullToken});
} else {
res.json({fullToken: null});
}
});
});
Browser Error:
OPTIONS http://127.0.0.1:3000/box/token/get No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access.
XMLHttpRequest cannot load http://127.0.0.1:3000/box/token/get. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:9000' is therefore not allowed access.

It seems the angular page was being served by server running on 127.0.0.1:9000. Cross origins policy disallows ajax requests from other domains. To get around it you can add express middleware which adds the headers for cross origin requests -
app.all('/*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,X-Requested-With');
next();
});

Oh dear, do not perform cross-origin requests!
There's an better way: grunt-connect-proxy - Issue requests to 9000 port and have your frontend server forward them to actual backend.
here's how to configure it in less than 3 minutes: https://stackoverflow.com/a/21655532/1432478

Related

Is CORS error specific to the nginx server or should I add something to my code

I recently deployed a website for the first time ever. We have 2 servers, so to say: 'https://baseUrl.com' and 'https://api.baseUrl.com' to make requests.
When trying to submit a contact form the data uploads to mongoDB but it isn't sent to node.js or from node.js it isn't sent to our e-mail address via nodemailer (I don't know exactly) and I get this error: Access to XMLHttpRequest at 'https://api.baseUrl.com' from origin 'https://baseUrl.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I do not have access to the nginx server but I was told that the specific CORS header/s have been set.
What should I do? Is there anything I could write in my code to fix this or it's strictly a server issue?
For example, I tried adding this code on node.js but it didn't help
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
Example of code that is not executed because of the CORS error:
app.post('/api/*endpoint*',(req,res) => {
upload(req,res,function(err){
if(err){
return res.end("Something went wrong!");
}else{
let mailOptions = {
from: req.body.email,
to: '*email*',
};
transporter.sendMail(mailOptions, function(err) {
if (err) {
return res(err);
}
})
}
})
});
you can use cors express middleware to avoid cors troubles. Sample usage is below. Before using, you have to install npm package by typing "npm i cors"
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})

The same node.js backend service, behaves different for the same request from different clients

There is a backend service which is resides on azure portal and developed with node.js by using Express.js
Requests are sending from an azure devops extension.
Most of the user's requests are successful but just a few users stuck on cors errors.
The cors error is
Access to fetch at ..... from origin .... has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
request resource. If an opaque response serves your needs, set the
request's mode to 'no-cors' to fetch the resource wiht CORS disabled.
A part of node.js server code (probably there are unnecessary parts in it)
var cors = require('cors')
app.use(cors())
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Type", "text/html");
next();
});
Successful request headers
Failed request headers
A get method example from client code
export function getTheValue() {
return fetch('https://....../.....')
.then(response => {
return response.text();
})
.catch(error => {
return reject(error);
})
}

Unable to send http post with credentials to iis node server

I'm trying to send a http post request from my angular app to my node.js server with user credentials in order to log it.
Both are hosted in IIS (the node server with nodeiis) and both are configured to be authenticated by windows authentication.
My angular code:
var url = "http://myurl:15001/addItem";
this.http.post(url, {
"itemName": "SomeName",
"itemColor": "SomeColor"
}, {withCredentials: true}).subscribe(res =>{
console.log("Great, item was added");
})
My Node.js Code:
var app = express();
var allowCrossDomain = function (req, res, next){
res.header('Access-Control-Allow-Origin', "http://myurl") //Cannot be a wildcard because of the credentials.
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if(res.method == 'OPTIONS')
res.send(200);
else
next();
};
app.use(allowCrossDomain);
app.post('/addItem', function(req, res){
//Saves the item...
res.setHeader('Access-Control-Allow-Origin', 'http://myurl')
res.status(200);
res.send(true);
});
When I do the request I get the following error to the console:
OPTIONS http://myurl:15001/addItem 401(Unauthorized)
XMLHttpRequest cannot load http://myurl:15001/addItem. Response to
preflight doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://myurl' is therefore bot allowed access. The
response had HTTP status code 401.
When I try to do the same thing with my http get request everything works properly and I get the result.
I don't know why my OPTIONS request is unauthorized when I send 200 code for every OPTIONS request.
I tried to use cors node.js package but it didn't help, maybe I didn't used that right.
Could someone explain me how can I solve this and make my http post pass the preflight? Thanks a lot!
If you are using Angular 6, it supports proxy.conf which will proxy the Backend API URL. For more details, here is the link https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md

Socket.io - Origin is not allowed access

I'm having this weird problem with socket.io. I have an express app which I run on port 5000. I have configured socket.io like this:
const app = require('../index');
const http = require('http');
const server = http.Server(app);
const io = require('socket.io')(server);
io.on('connection', function (socket) {
console.log('User has connected');
socket.emit('connect', {
message: 'Hello World'
});
});
Then I import this piece of code into my index.js file like this:
const express = require('express');
const app = module.exports = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const request = require('request');
const boxRoutes = require('./routes/v1/boxRoutes');
const bidRoutes = require('./routes/v1/bidRoutes');
// use body parser so we can get info from POST and/or URL parameters
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
app.use(bodyParser.json({ limit: '10mb' }));
require('./services/usersClass');
// cors set up
app.use(cors());
app.use(function (req, res, next) {
console.log('Headers Middleware Called');
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'origin, x-requested-with, content-type, accept, x-xsrf-token', 'token');
// 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);
// Request headers you wish to expose
res.setHeader('Access-Control-Expose-Headers', false);
next();
});
// Middleware to authenticate the requests to this service
app.use(function(req, res, next) {
console.log('Auth Middleware Called');
if(!req || !req.headers['authorization']) return res.sendStatus(401);
const token = req.headers['authorization'].split(' ')[1];
request.post(
'http://localhost:4000/api/v1/users/auth',
{
headers: {
'Authorization': `Bearer ${token}`
}
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
const data = JSON.parse(body);
res.locals.user = data.user;
next();
} else {
console.log('Request has failed. Please make sure you are logged in');
res.sendStatus(401);
}
}
);
});
app.use('/api/v1/boxes/', boxRoutes);
app.use('/api/v1/bids/', bidRoutes);
// disable 'powered by'
app.disable('x-powered-by');
app.listen(5000, () => {
console.log('Trading service is running on port 5000');
});
Now, in my client code, I try to establish socket.io connection when the user logs in. Everytime I try to connect to the server, I get the following error:
Failed to load
http://localhost:5000/socket.io/?EIO=3&transport=polling&t=MA_9wXE:
Response to preflight request doesn't pass access control check: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'. Origin 'http://localhost:3000' is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
I don't understand why the connection fails. I have configured Access-Control-Allow-Origin to my client domain but it still fails.
You can use cors npm module. It will fix your problem.
var cors = require('cors')
var app = express()
app.use(cors({origin: '*'}))
start '*' means allow every origins. You can type spesific origin too.
I've seen this problem before, but never seen it manifested as a cross origin issue. You are creating two separate http servers. One you are making your express server and the other you are making your socket.io server. The code you show only actually starts the express server and you show no code that actually starts your socket.io server.
Here's where you create these two separate servers:
const server = http.Server(app); // creates the http server you use for socket.io
app.listen(5000, () => {...}); // creates the http server you use with Express
Inside of app.listen(), it creates it's own new server and starts it. Your other server is never started (at least per the code you show here).
When you probably want to do is to make your socket.io server use the same server as your express server and then you should be able to connect just fine without any CORs issues.
If you want to use app.listen(), it will return the server object that it created and you need to use that to initialize socket.io.
If you want to use the other server, then you need to share that with your express initialization code so it can use that one.

AngularJS $resource makes HTTP OPTIONS request instead of HTTP POST for $save method

I'm in the process of writing a simple library application to get ready for a larger project with AngularJS. After reading a lot online about using $resource to interact with a RESTful API, I decided that it would probably offer some time-saving and scaling benefits to implement it instead of using $http for each request. The problem is that for some reason (I'm no expert on CORS and the request is being sent cross-domain) when using the $save method my Node.js console shows:
OPTIONS /books 200 1ms - 161b
Using the query() method works fine - the Node console shows:
GET /books 200 1ms - 228b
I've been stuck for several hours at this point, trying variations on the below but it always ends up being an OPTIONS request instead of POST (which is what it should be according to the Angular documentation) for the $save method.
AngularJS Web App
app.js
var libraryApp = angular.module('libraryApp', ['ngResource', 'ngRoute', 'libraryControllers']);
libraryApp.factory('$book', ['$resource', function ($resource) {
return $resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' });
}]);
controllers.js
var libraryControllers = angular.module('libraryControllers', []);
libraryControllers.controller('BookCtrl', ['$scope', '$book', function($scope, $book) {
...
$scope.addBook = function () {
var b = new $book;
b.isbn = "TEST";
b.description = "TEST";
b.price = 9.99;
b.$save();
};
}]);
Node.js with Express REST API
app.js
var express = require('express'),
books = require('./routes/books'),
http = require('http'),
path = require('path');
var app = express();
...
// enable cross-domain scripting
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", req.headers.origin);
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
// routing
app.get('/books', books.getAll);
app.get('/books/:isbn', books.get);
// This is what I want to fire with the $save method
app.post('/books', books.add);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
./routes/books.js
...
exports.add = function(req, res) {
console.log("POST request received...");
console.log(req.body.isbn);
};
Tried putting this line in my config function delete $httpProvider.defaults.headers.common["X-Requested-With"]; but no change.
I'm no Angular/Node pro but right now I'm thinking that it's something to do with it being cross domain and, like I said, I'm no expert on CORS.
Thanks in advance.
I know it may be in bad taste to answer my own question but I figured out the problem a few days after posting this.
It all comes down to how browsers manage CORS. When making a cross-domain request in JavaScript that is not "simple" (i.e. a GET request - which explains why the query() function worked), the browser will automatically make a HTTP OPTIONS request to the specified URL/URI, called a "pre-flight" request or "promise". As long as the remote source returns a HTTP status code of 200 and relevant details about what it will accept in the response headers, then the browser will go ahead with the original JavaScript call.
Here's a brief jQuery example:
function makeRequest() {
// browser makes HTTP OPTIONS request to www.myotherwebsite.com/api/test
// and if it receives a HTTP status code of 200 and relevant details about
// what it will accept in HTTP headers, then it will make this POST request...
$.post( "www.myotherwebsite.com/api/test", function(data) {
alert(data);
});
// ...if not then it won't - it's that simple.
}
All I had to do was add the details of what the server will accept in the response headers:
// apply this rule to all requests accessing any URL/URI
app.all('*', function(req, res, next) {
// add details of what is allowed in HTTP request headers to the response headers
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Credentials', false);
res.header('Access-Control-Max-Age', '86400');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
// the next() function continues execution and will move onto the requested URL/URI
next();
});
And then insert these few lines before the Express routing to simply return a HTTP 200 status code for every OPTIONS request:
// fulfils pre-flight/promise request
app.options('*', function(req, res) {
res.send(200);
});
Hopefully this will help anyone who stumbles on this page suffering from the same problem.
I didn´t actually try this, but wouldn´t it be enough to tell the Ressource how to handle the $save request?
$resource('http://mywebserver\\:1337/books/:bookId', { bookId: '#bookId' }, {save: {method: 'POST'});

Resources