I am a beginner in server side programming. Trying to write code that will allow me to get data from state variable, send it to my backend and use nodemailer service to send it to an email adress. My frontend is as follows :
const handleSubmit = async () => {
try {
await axios.post(
"http://localhost:2525/send_mail",
{
state
}
);
} catch (error) {
console.log(error);
}
};
state gets sent to backend :
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const nodemailer = require('nodemailer');
require('dotenv').config();
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
app.options('',cors());
app.get('/', (req, res) => {
res.send('Server is running');
});
app.post("/send_mail", cors(), async (req, res) => {
let { state } = req.body;
const transport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'adress#mail.com',
pass:'password'
}
});
await transport.sendMail({
from: '<myemail.#example.com>',
to: "reciever#example.com",
subject: "test email",
html: `<div><p>${state.Message}</p></div>`
});
});
app.listen(process.env.PORT || 2525, () => { console.log("Server is running"); });
If it matters, state is an object that gets filled from form data, it looks something like :
const data = {
FirstName: "",
LastName: "",
Message:"",
};
When i visit port 2525, server is indeed running with the message i gave it. But when i try to submit my form with "handleSubmit", i get the following console error:
*>
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:2525/send_mail. (Reason: CORS request did not succeed). Status code: (null).*
And it crashes the server
As you can see i am already using cors middleware to try to handle it.
I tried adding a proxy to the package.json file, as : "proxy": "http://localhost:2525", ( i did not change anything else when i did that, i don't know if that is the correct way). This did not solve anything, but at least the server did not crash any more. Instead, the POST request gets a 404:
*
POSThttp://localhost:2525/send_mail Referrer Policystrict-origin-when-cross-origin*
I tried running it in Chrome, same problem. I tried everything i could read on stackoverfrlow google or chat gpt, really that is cors-related. Even checked if port is maybe taken by something else in windows, checked that in cmd. Im at my wits end really
I have a problem between frontend backend and Flask API.
To execute my project I do `npm start. This will run the ReactJs frontend dev server on port 3000.
In package.json
I added the following "proxy": "http://localhost:5000",
Next, I do
cd backend && python server.py after activating my venv
This will run the Flask API on port 5000
The Flask API has this route
from flask_cors import cross_origin
# File download Link
#app.route('/filePath', methods=['POST'])
#cross_origin()
def get_path():
data = request.get_json()["path"]
storage.child(f"files/{data}").download(f"files/Resume.pdf")
return "Success.."
Finally, in another shell I do
cd backend && node server.js running on port 8080
Which has the following post
app.post('/insert', async (req, response) => {
const mobile_number = req.body.college_name
const name = req.body.college_name
axios.get('http://localhost:3000/details').then(async (res) => {
const recruit = new RecruitModel({
mobile_number:res.data.mobile_number, name:res.data.name,
});
await recruit.save()
response.send("inserted data")
});
});
Here is where the error happens in the frontend.
const uploadFiles = (file) => {
//
if (!file) return;
if (!initialData) return null;
const storageRef = ref(storage, `files/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
"state_changed",
(snapshot) => {
const prog = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
setProgress(prog);
},
(error) => console.log(error),
() => {
getDownloadURL(uploadTask.snapshot.ref).then(async () => {
console.log(file.name);
await axios.post('http://localhost:5000/filePath', {
'path': file.name
}).then(() => console.log(file.name));
update();
});
}
);
};
In await axios.post('http://localhost:5000/filePath' I'm assuming.
I get the following error:
Access to XMLHttpRequest at 'http://localhost:8080/insert' from origin
'http://localhost:3000' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: The
'Access-Control-Allow-Origin' header has a value
'http://localhost:5000' that is not equal to the supplied origin
I thought using flask-cors would fix this so I'm not sure why I'm getting this error.
I'm really struggling on this error, Any suggestions please?
EDIT
const addRecruit = () => {
axios.post("http://localhost:8080/insert", {
college_name:initialData.college_name,
email:initialData.email,
mobile_number:initialData.mobile_number, name:initialData.name
});
}
This is where the issue is happening. Because the data is being fetched between Flask and ReactJs but this /insert is in server.js
The EDIT made things clear, the issue isn't to do with your Flask api. You need to enable cors in server.js
I'm assuming you had this setup before,
const cors = require('cors');
app.use(cors());
Do this instead
const corsOptions ={
origin:'http://localhost:3000',
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
app.use(cors(corsOptions));
Don't forget to npm i cors
To start the Bot Framework webchat client, I need to 'get' a token from a restify server running in Azure. The server fetches the token (using my secret) from https://webchat.botframework.com/api/tokens. The last part works fine. The first part didn't. I did not manage to get the webchat client to accept the response from the restify server. Offline (localhost) works fine. When the server is running online (Node in Azure) I get:
Access to fetch at 'https://mybot.azurewebsites.net/api/token' from origin 'https://mydomainname.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In the dev tool of chrome I can see that with restify server running on local host, I get an access-control-allow-origin and an access-control-expose-header. When running on Node (in Azure) I only get access-control-expose-header.
What did I already try (without a good result):
Implement cors middleware for restify server
Use Express in stead of restify
add headers manually to the response: res.send(token, { 'Access-Control-Allow-Origin': '*' })
specify the domain name in the (trusted) origins list of Cors middelware
[the solution] add the domain name running the calling javascript in the list with approved origins in the azure app service running the restify service.
My current code (with the Cors middleware)
Restify server
const fetch = require('node-fetch');
const restify = require('restify');
const path = require('path');
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
origins: ['*']
});
// Create HTTP server and Cors
let server = restify.createServer();
server.use(cors.actual);
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }.`);
console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator.`);
});
// Listen for bot requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (context) => {
await bot.run(context);
});
});
// Listen for token requests.
server.get('/api/token',
async function(req, res) {
const result = await fetch('https://webchat.botframework.com/api/tokens', {
method: 'GET',
headers: {
Authorization: `Bearer ${ process.env.directLineSecret }`
}
});
const token = await result.json();
console.log(token);
res.send(token);
});
The webchat client
In this code snippet, the client is talking to a server running on local host. This works. As soon as we talk to a server hosted on Azure, it doesn't work anymore (due to cors it seems)
(async function () {
const res = await fetch('http://localhost:3978/api/token', { method: 'GET' });
const webChatToken = await res.json();
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token: webChatToken })
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
Someone any thoughts on how to fix this?
Had me searching for a day and trying lots of options.
The solution was in Azure App Service (Node) CORS origins won't work no matter where I add them
You have to add the calling domain in the list of approved origins in the CORS menu of the appservice running your restify service.
Not sure if this makes sense, but it helped.
To avoid same-domain AJAX issues, I want my node.js web server to forward all requests from URL /api/BLABLA to another server, for example other_domain.com:3000/BLABLA, and return to user the same thing that this remote server returned, transparently.
All other URLs (beside /api/*) are to be served directly, no proxying.
How do I achieve this with node.js + express.js? Can you give a simple code example?
(both the web server and the remote 3000 server are under my control, both running node.js with express.js)
So far I found this https://github.com/http-party/node-http-proxy , but reading the documentation there didn't make me any wiser. I ended up with
var proxy = new httpProxy.RoutingProxy();
app.all("/api/*", function(req, res) {
console.log("old request url " + req.url)
req.url = '/' + req.url.split('/').slice(2).join('/'); // remove the '/api' part
console.log("new request url " + req.url)
proxy.proxyRequest(req, res, {
host: "other_domain.com",
port: 3000
});
});
but nothing is returned to the original web server (or to the end user), so no luck.
request has been deprecated as of February 2020, I'll leave the answer below for historical reasons, but please consider moving to an alternative listed in this issue.
Archive
I did something similar but I used request instead:
var request = require('request');
app.get('/', function(req,res) {
//modify the url in any way you want
var newurl = 'http://google.com/';
request(newurl).pipe(res);
});
I found a shorter and very straightforward solution which works seamlessly, and with authentication as well, using express-http-proxy:
const url = require('url');
const proxy = require('express-http-proxy');
// New hostname+path as specified by question:
const apiProxy = proxy('other_domain.com:3000/BLABLA', {
proxyReqPathResolver: req => url.parse(req.baseUrl).path
});
And then simply:
app.use('/api/*', apiProxy);
Note: as mentioned by #MaxPRafferty, use req.originalUrl in place of baseUrl to preserve the querystring:
forwardPath: req => url.parse(req.baseUrl).path
Update: As mentioned by Andrew (thank you!), there's a ready-made solution using the same principle:
npm i --save http-proxy-middleware
And then:
const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', {target: 'http://www.example.org/api'});
app.use(apiProxy)
Documentation: http-proxy-middleware on Github
You want to use http.request to create a similar request to the remote API and return its response.
Something like this:
const http = require('http');
// or use import http from 'http';
/* your app config here */
app.post('/api/BLABLA', (oreq, ores) => {
const options = {
// host to forward to
host: 'www.google.com',
// port to forward to
port: 80,
// path to forward to
path: '/api/BLABLA',
// request method
method: 'POST',
// headers to send
headers: oreq.headers,
};
const creq = http
.request(options, pres => {
// set encoding
pres.setEncoding('utf8');
// set http status code based on proxied response
ores.writeHead(pres.statusCode);
// wait for data
pres.on('data', chunk => {
ores.write(chunk);
});
pres.on('close', () => {
// closed, let's end client request as well
ores.end();
});
pres.on('end', () => {
// finished, let's finish client request as well
ores.end();
});
})
.on('error', e => {
// we got an error
console.log(e.message);
try {
// attempt to set error message and http status
ores.writeHead(500);
ores.write(e.message);
} catch (e) {
// ignore
}
ores.end();
});
creq.end();
});
Notice: I haven't really tried the above, so it might contain parse errors hopefully this will give you a hint as to how to get it to work.
To extend trigoman's answer (full credits to him) to work with POST (could also make work with PUT etc):
app.use('/api', function(req, res) {
var url = 'YOUR_API_BASE_URL'+ req.url;
var r = null;
if(req.method === 'POST') {
r = request.post({uri: url, json: req.body});
} else {
r = request(url);
}
req.pipe(r).pipe(res);
});
I used the following setup to direct everything on /rest to my backend server (on port 8080), and all other requests to the frontend server (a webpack server on port 3001). It supports all HTTP-methods, doesn't lose any request meta-info and supports websockets (which I need for hot reloading)
var express = require('express');
var app = express();
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var backend = 'http://localhost:8080',
frontend = 'http://localhost:3001';
app.all("/rest/*", function(req, res) {
apiProxy.web(req, res, {target: backend});
});
app.all("/*", function(req, res) {
apiProxy.web(req, res, {target: frontend});
});
var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) {
apiProxy.ws(req, socket, head, {target: frontend});
});
server.listen(3000);
First install express and http-proxy-middleware
npm install express http-proxy-middleware --save
Then in your server.js
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use(express.static('client'));
// Add middleware for http proxying
const apiProxy = proxy('/api', { target: 'http://localhost:8080' });
app.use('/api', apiProxy);
// Render your site
const renderIndex = (req, res) => {
res.sendFile(path.resolve(__dirname, 'client/index.html'));
}
app.get('/*', renderIndex);
app.listen(3000, () => {
console.log('Listening on: http://localhost:3000');
});
In this example we serve the site on port 3000, but when a request end with /api we redirect it to localhost:8080.
http://localhost:3000/api/login redirect to http://localhost:8080/api/login
Ok, here's a ready-to-copy-paste answer using the require('request') npm module and an environment variable *instead of an hardcoded proxy):
coffeescript
app.use (req, res, next) ->
r = false
method = req.method.toLowerCase().replace(/delete/, 'del')
switch method
when 'get', 'post', 'del', 'put'
r = request[method](
uri: process.env.PROXY_URL + req.url
json: req.body)
else
return res.send('invalid method')
req.pipe(r).pipe res
javascript:
app.use(function(req, res, next) {
var method, r;
method = req.method.toLowerCase().replace(/delete/,"del");
switch (method) {
case "get":
case "post":
case "del":
case "put":
r = request[method]({
uri: process.env.PROXY_URL + req.url,
json: req.body
});
break;
default:
return res.send("invalid method");
}
return req.pipe(r).pipe(res);
});
I found a shorter solution that does exactly what I want https://github.com/http-party/node-http-proxy
After installing http-proxy
npm install http-proxy --save
Use it like below in your server/index/app.js
var proxyServer = require('http-route-proxy');
app.use('/api/BLABLA/', proxyServer.connect({
to: 'other_domain.com:3000/BLABLA',
https: true,
route: ['/']
}));
I really have spent days looking everywhere to avoid this issue, tried plenty of solutions and none of them worked but this one.
Hope it is going to help someone else too :)
I don't have have an express sample, but one with plain http-proxy package. A very strip down version of the proxy I used for my blog.
In short, all nodejs http proxy packages work at the http protocol level, not tcp(socket) level. This is also true for express and all express middleware. None of them can do transparent proxy, nor NAT, which means keeping incoming traffic source IP in the packet sent to backend web server.
However, web server can pickup original IP from http x-forwarded headers and add it into the log.
The xfwd: true in proxyOption enable x-forward header feature for http-proxy.
const url = require('url');
const proxy = require('http-proxy');
proxyConfig = {
httpPort: 8888,
proxyOptions: {
target: {
host: 'example.com',
port: 80
},
xfwd: true // <--- This is what you are looking for.
}
};
function startProxy() {
proxy
.createServer(proxyConfig.proxyOptions)
.listen(proxyConfig.httpPort, '0.0.0.0');
}
startProxy();
Reference for X-Forwarded Header: https://en.wikipedia.org/wiki/X-Forwarded-For
Full version of my proxy: https://github.com/J-Siu/ghost-https-nodejs-proxy
I think you should use cors npm
const app = express();
const cors = require('cors');
var corsOptions = {
origin: 'http://localhost:3000',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));
https://www.npmjs.com/package/cors
So I'm running it on port 8080. Port forwarding has been set up and it is working.
Every time I type in my no-ip domain, I get the response on the screen but when I'm making a request from my website, it logs the request on the Raspberry, yet, there is no response visible in the Chrome developer tools.
I also get this error message: POST "name of the api" net::ERR_EMPTY_RESPONSE
What could cause that? My routes worked perfectly when I was running my api locally.
module.exports = function(app) {
app.get('/', requireAuth, function(req, res) {
res.send({ message: 'OMG, You made it, you deserve a drink!' });
});
That's how my react app looks like:
const ROOT_URL = *"name of the api"/*;
.
.
.
export function fetchMessage() {
return function(dispatch) {
axios.get(ROOT_URL, {
headers: { authorization: localStorage.getItem('token') }
})
.then(response => {
dispatch({
type: FETCH_MESSAGE,
payload: response.data.message
});
});
}
};
Is it a typical problem of the Node.js, Express, React or maybe it's on the Raspi? Thanks a lot!
Possibly a CORS issue, since the problem only happens when trying to consume the API from the browser. A possible solution is to use the cors package in your Express application:
const express = require('express');
const cors = require('cors');
...
const app = express();
app.use(cors());
...
NOTE: this enables all CORS requests.