0
votes

front end: localhost:3000 (react app) App.js (client) on load call api '/'

function fetch_redirect() {
  fetch("http://localhost:8082")
}

function App() {
  return <div className="App">{fetch_redirect()}</div>;
}

export default App;

backend: localhost:8082 (nodejs express app) send redirect to /test endpoint on client

const express = require('express')
const app = express()
const cors = require('cors');

const whitelist = ['http://localhost:3000']
const corsOptions = {
  origin: function (origin, callback) {
      console.log(origin, whitelist.indexOf(origin) !== -1);
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  }
}

app.use(cors(corsOptions))
app.get('/', function (req, res) {
          res.redirect('http://localhost:3000/test')
    })
     
    app.listen(8082)

The main issue is on the client (react) once the API call is made, the redirect is blocked... how can I make the client react app follow this redirect to 'http://localhost:3000/test' I already have cors on my backend with an enabled whitelist of localhost:3000 to allow me to make the API call not blocked... however the redirect is now blocked from the fetch frontend!

error details: Access to fetch at 'http://localhost:3000/test' (redirected from 'http://localhost:8082/') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

4
What are you using to run react app? I copied your code and run client with vite and everything works fine. You probably need to modify your5 client dev serverMr. Hedgehog
create-react-app with npm start commanduser2950720
To make this work, I think you’d need to change the localhost:8082 express code to allow all origins — not just http://localhost:3000. The reason is, when a request gets redirected across origins, the browser sets the Origin request header to null. So when the localhost:8082 express code sees the request, the Origin value is null, not http://localhost:3000.sideshowbarker

4 Answers

1
votes

In order to facilitate the development and have a development environment closer to what the production environment should be (no need to decrease the security level with Access-Control-Allow-Origin), you should have a unique entry point to your frontend and backend, with a unique origin (http://localhost:3000 in your case).

To achieve that, remove all of the Access-Control-* headers, create a src/setupProxy.js file and fill it like this:

const { createProxyMiddleware } = require("http-proxy-middleware");

module.exports = app => {
  app.use(
    "/api",
    createProxyMiddleware ({
      target: "http://localhost:8082",
      changeOrigin: true
    })
  );
};

Then install the needed express middleware as a development dependency with npm install --save-dev http-proxy-middleware

Note: this feature is available with [email protected] and higher.

Finally, replace all of the fetch from the frontend with relative URLs starting with /api

  • fetch("http://localhost:8082") becomes fetch('/api'))
  • Something like fetch('http://localhost:8082/some-endpoint') would become fetch('/api/some-endpoint')

For more information about this, please refer to the docs.

0
votes

Try to refactor your code like this:

const express = require('express')
const app = express()
const cors = require('cors');


const whitelist = ['http://localhost:3000', 'http://localhost:8082'];


const corsOptionsDelegate = (req, callback) => {
  var corsOptions;
  if (whitelist.indexOf(req.header('Origin')) !== -1) {
    corsOptions = { origin: true };
  } else {
    corsOptions = { origin: false };
  }
  callback(null, corsOptions);
};

const corsWithOptions = cors(corsOptionsDelegate);

app.route('/')
  .options(corsWithOptions, (req, res) => { res.sendStatus(200); })
  .get(cors(), (req, res)=>{
       res.redirect('http://localhost:3000/test')
});


app.listen(8082)

0
votes

Try using next function inside app.get:

app.get('/', function (req, res, next) {
  res.redirect('http://localhost:3000/test');
  next();
})

Also for your ReactJS application (I'm not sure about your implementation, CSR or SSR) you should add two things:

  • Webpack: proxy for passing CORS
    • CSR:
      devServer: {
        ...
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
          "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
        }
      }
      
    • SSR: add the above header to webpackDevMiddleware options.
  • Route:
    • CSR: for running the project in the dev side use:
      devServer: {
         historyApiFallback:{
           index:'build/index.html'
         },
      },
      
      for the prod side use spa flag for running, for example for serve use serve -s ./dist or for pm2 run pm2 start ./dist --spa.
    • SSR: no need to add extra configuration.
-1
votes

try something like this instead:

whitelistedOrigins = ['http://localhost:3000'];
app.use(cors({
    origin: whitelistedOrigins,
    methods: ['GET', 'PATCH', 'PUT', 'POST', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
}));

you can modify the methods/allowedHeaders keys as you want...

Edit: your backend is :3000, isn't it the problem? the client can't be at 3000 at the same time.