I have just started with Node.
I am trying to get cross-domain form data from an HTML form to parse in a Node.js server. I have been able to do this with simple POST data, not with POST requests that require preflight.
I am running the Node code on cloud9 app servers. I am also using the Cors module to handle the requests. This module works well with simple requests (test here to see a simple request work), however with requests that require preflight I get this result from the Chrome inspector console.
XMLHttpRequest cannot load https://nms-motaheri-1.c9.io:8080/mail.
The request was redirected to 'https://c9.io:8080/api/nc/auth?.....SHORTENED',
which is disallowed for cross-origin requests that require preflight.
Here is my server.js code:
// Define dependencies
var express = require('express')
, cors = require('cors')
, app = express()
, parse_post = require("parse-post");
// Core module config
var corsOptions = {
origin: '*',
preflightContinue: true // <- I am assuming this is correct
};
app.use(cors(corsOptions));
// Respond to option request with HTTP 200
// ?? Why is this not answering my OPTION requests sufficiently ??
app.options('*',function(req,res){
res.send(200);
});
// Give a hello world response to all GET requests
app.get('/',function(req,res){
res.sendFile(__dirname + '/index.html');
});
// Handle all POST requests to /mail
app.post('/mail', parse_post(function(req, res) {
console.log(req.body);
res.json({msg: 'This is CORS-enabled for all origins!'});
})
);
// Listen on default Cloud9 port which is 8080 in this case
app.listen(process.env.PORT, function(){
console.log('CORS-enabled web server listening on port ' + process.env.PORT);
});
Why is this happening and how can I satisfactorily answer the OPTION request for my POST with pre-flight?
Here is the post request and response in Chrome dev tools:
Turns out that part of the problem was that the cloud9 server was set to private making these requests all redirect.
After making the server public, the redirections stopped. However, I received an error that the Node.js server did not have any Access-Control-Allow-Origin headers to allow requests from my cross origin domain. I noticed that "simple" with-out preflight requests would go through. So instead of trying to understand why it was not accepting my allow-all-origin-configuration on the Node.js side I decided to serialized the POST data to get rid of the preflight requirement and changed the data type in my angular request to plain text.
To get rid of preflight, first get rid of any POST header configuration (cache, etc), make sure your request Content-Type is plain text and make sure your actual content is plain text too. So if it is in JSON serialize it in jQuery before sending it with POST.
This is what my new Angular Post request code looked like:
sendEmail: function(email) {
var config = {
headers: {
'Content-Type': 'text/plain'
}
};
var POSTDATA= JSON.stringify(POSTDATAJSON);
return $http.post(POSTURL, POSTDATA, config)
}
And in Node.js this, I am using the cors Node.js module:
app.post('/mail', parse_post(function(req, res) {
var postReq = JSON.parse(Object.keys(req.body));
}));
Related
I can't get any POST requests with the express framework.
This is my code
var express = require("express");
var app = express();
app.set("view engine","ejs")
app.get("/", function(req, res){
res.render("home");
});
app.post("/addfriend", function(req, res){
res.send("you have reached the post route succesfully");
});
app.get("/friends", function(req, res){
var friends =["lara","tommy","miranda","faith","locas"];
res.render("friends",{friends : friends});
});
app.listen(3000, function(){
console.log("server is listening on port 3000");
});
any suggestion please.
Few observations.
1)You are missing body parser for your app.js ( if in future you want to read form data).
just add this to your app,js
const app = express();
app.use(bodyParser.json());
Wherever you are using trying to send post request for example a HTML form or a button does it have correct route, it should match with this post route /addfriend.
Browser always send get request to server from browser, to specifically send post request use postman, curl or a HTML form.
If you are using postman try this https://learning.postman.com/docs/sending-requests/requests/#sending-body-data
For the difference between get and post follow this
Edit :-
Http post request means :- "The HTTP POST method sends data to the server. The type of the body of the request is indicated by the Content-Type header."
get request :- "The HTTP GET method requests a representation of the specified resource. Requests using GET should only be used to request data (they shouldn't include data)."
I have a frontend-only web application on Netlify which has to consume an API on OpenSubtitles.org. Although OpenSubtitles.org enables CORS, sometimes I got preflight errors, so I decided to use a proxy.
I had problems using Netlify's proxy feature, so I decided I will create my own proxy on Heroku, and send my requests from the frontend to there, so these will be proxied to OpenSubtitles.org from a server.
I came up with the following based on the code I found here:
const express = require('express');
const request = require('request');
express()
.use('/', function(req, res) {
req.pipe(
request({
url: 'http://rest.opensubtitles.org/search' + req.url,
headers: {
'User-Agent': 'TemporaryUserAgent'
}
})
).pipe(res);
})
.listen(process.env.PORT || 8000);
I thought I deploy this, try it out, then I will enable CORS on it after that. However I've just realized it is working perfectly without doing anything else. How is it possible? Why can I call this from a frontend-only app on a different domain without explicitly enabling CORS?
Also, what if the server crashes, how to handle the errors there?
CORS is working because the url you're requesting responds with the header Access-Control-Allow-Origin set with a value of *. Since you're piping that response and its headers back to the original res object, it will enable CORS as if it was coming from your local proxy.
Below is a more straightforward example of how to proxy a request to another site and return its response intact using node streams.
const express = require('express')
const request = require('request')
const port = process.env.PORT || 1337
let server = express()
const proxyMiddleware = (req, res, next) => {
let url = `https://www.google.com/${req.url}`
let proxyRequest = request(url)
// Pass request to proxied request url
req.pipe(proxyRequest)
// Respond to the original request with the response from proxyRequest
proxyRequest.pipe(res)
}
server.use(proxyMiddleware)
server.listen(port, () => console.log(`Listening on ${port}`))
I have some issues with a server that does not support IPv6 requests from Apple Application Review. So they reject my update.
And i'm thinking of making a request handler as a middle server, with nodejs.
So my app will send the requests in my new server, which server will send the request to the old server, take the response json back, and serve it back as well in my app.
So lets say the old webserver request was the following
https://www.example.com/example/api/index.php?action=categories&subaction=getproducts&category_id=100304&limit=0,30
But the request parameters are not always the same!
It may vary but the main URL is always the same
https://www.example.com/example/api/index.php?
The question is how to get the request params dynamically, make a request to the old webserver and return the response to the request of the new webserver?
You just need a very simple proxy like this;
const express = require('express')
const request = require('request')
const app = express()
const BASE_URL = 'http://www.google.com' // change accordingly
app.use('/', (req, res) => {
request({
url: BASE_URL + req.originalUrl
}).pipe(res)
})
app.listen(8900, () => console.log('Listening...'))
req.originalUrl will allow to concatenate the path + the query string to your base url
I've got this function in my component, that's supposed to send a POST request to an express API:
onSubmit (evt) {
evt.preventDefault();
//alert(JSON.stringify(this.description));
axios.post('http://localhost:3000/api/v1/addComment', {
articleid: this.id,
description: this.description
});
}
This is the API to which the request is being sent:
router.post('/api/v1/addComment/', function(req, res){
var newComment = req.body;
//newComment.id = parseInt(commentsData.length);
commentsData.comments.push(newComment);
fs.writeFile('app/data/comments.json', JSON.stringify(commentsData), 'utf8', function(err){
console.log(err);
});
res.setHeader("Access-Control-Allow-Origin", "*");
res.json(newComment);
});
I've also required the the neccessary CORS dependency in my express, app.js file
var express = require('express');
var reload = require('reload');
var app = express();
var cors = require('cors');
var dataFile = require('./data/articles.json');
app.set('port', process.env.PORT || 3000 );
//app.set('appData', dataFile);
app.set('view engine', 'ejs');
app.set('views', 'app/views');
app.use(express.static('app/public'));
app.use(require('./routes/index'));
app.use(require('./routes/comments'));
app.use(cors());
var server = app.listen(app.get('port'), function(){
console.log('Listening on port ' + app.get('port'));
});
reload(server, app);
The API routes work fine, when I do get requests, however, I keep getting this error when I do a post request:
Failed to load http://localhost:3000/api/v1/addComment: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed
access. createError.js?16d0:16 Uncaught (in promise) Error: Network Error at createError (createError.js?16d0:16) at XMLHttpRequest.handleError (xhr.js?ec6c:87)
I've also tried sending a headers object along with the axios post request, but to no avail.
Based on my research into CORS, I understand that Using CORS is neccessary to allow requests to your API, coming from a different domain.
My express server runs on localhost 3000, while my vue server runs at local host 8080.
Could someone explain why I'm still getting this error despite requiring and using CORS in express?
Try moving the
app.use(cors())
up before you assign the routes
The pre-flight part is referring to xhr making a OPTIONS request to /api/v1/addComment before the actual POST. You'll need to configure cors to handle that:
// preflight for aspecific route
router.options('/api/v1/addComment/', cors())
// or preflight for all routes
router.options('*', cors())
Note: you'll want to make these calls before defining the rest of the routes. See the docs on npmjs.
What I am trying to do:
Proxy a java api that runs on https://127.0.0.1:443/api/ along side my UI that runs on non-SSL http://127.0.0.1:1337/ in order to circumnavigate some CORS issues.
My attempt:
Proxy the api at the SSL port 443 to my non-SSL development port of 1338.
proxy my UI to 1337
Proxy 1137 to :8080/index.html and proxy 1338 to :8080/api/
Access my app from localhost:8080
My problem:
The UI comes in just fine... but I can not hit the API at :8080/api/httpSession/init
Yes, I can still hit the API at https://localhost/api/httpSession/init
api.js - Renders index.html at :1337
var app = express();
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
var options = {
changeOrigin: true,
target: {
https: true
}
};
httpProxy.createServer(443, '127.0.0.1', options).listen(1338);
start.js - Proxies 1337 and 1338 into 8080
// First I start my two servers
uiServer.start(); // renders index.html at 1337
apiServer.start(); //
// I attempt to patch them back into one single non-SSL port.
app
.use('/', proxy({target: 'http://localhost:1337/'}))
.all('/api/*', proxy({target: 'http://localhost:1338/'}))
.listen(8080, function () {
console.log('PROXY SERVER listening at http://localhost:%s', 8080);
});
What you're looking for is request piping. Try this example:
// Make sure request is in your package.json
// if not, npm install --save request
var request = require('request');
// Intercept all routes to /api/...
app.all('/api/*', function (req, res) {
// Get the original url, it's a fully qualified path
var apiPath = req.originalUrl;
// Form the proxied URL to your java API
var url = 'https://127.0.0.1' + apiPath;
// Fire off the request, and pipe the response
// to the res handler
request.get(url).pipe(res);
});
Make sure to add some error handling if the api can't be reached, such as this SO solution.
For the proxy issue, my guess is that it is keeping the /api/* in the url and that's not present on the router in your API service. You could try adding /api to the router in the API service since it's going to keep the url string the same when it sends it. Otherwise, you likely need to proxy and rewrite the url so that the API will match the request to a route.
On another note, what about just installing the cors module and using in the app? I do something similar and it's working well without all the proxy items. https://www.npmjs.com/package/cors