【问题标题】:MERN Stack - Express and React on same port?MERN Stack - Express 和 React 在同一个端口上?
【发布时间】:2018-11-08 12:33:35
【问题描述】:

我正在处理一个使用 MERN(MongoDB、Express、React、Node)堆栈的项目,并且在将数据从 React 组件中的表单发布到 Node.js 中定义的 API 端点时遇到问题。当我提交表单时,浏览器只显示一个 CANNOT POST 错误。我非常有信心,如果我在 React 中为表单提交创建一个事件处理程序并使用 Axios 等库处理 POST,我可以解决这个问题。

但最终我相信这个问题是因为 Node 后端运行在与 React 前端不同的端口上。有没有一种方法可以配置我的堆栈,以便我可以使用标准形式的 POST 并可能让 FE 和 BE 在同一个端口上运行?

【问题讨论】:

  • 没有其他错误详情吗?您不能在同一端口上运行两个不同的应用程序(服务器)。你用什么做 React 开发服务器?
  • 如果不是同一个 express 应用,你的 React 前端是什么?如果您确实是故意使用另一个网络服务器/应用程序为其提供服务,那么您必须设置从该服务器到您的快速服务器的重定向。
  • 我在前端使用未弹出的 create-react-app 并为后端使用 express
  • 所以,不要想着在同一个端口上运行两台服务器(这是不可能的),使用代理从 React 端发出 API 请求。

标签: node.js reactjs mern


【解决方案1】:

为了开发,将以下行添加到 package.json 文件中

"proxy": "http://localhost:{your API port}/"

对于生产,您可以在应用程序(Express、Nginx、...)中设置代理,它将为您的静态文件(React 应用程序、样式等)提供服务。通常使用“/api/”掩码来判断API请求。

【讨论】:

  • 我确实已经有了这条线,但是当我尝试 POST 表单数据时,我仍然收到 CANNOT POST 错误。
【解决方案2】:

我看到您正在运行未弹出的 CRA。这意味着当您从 create-react-app 文件夹运行 npm run start 时,您应该在默认端口 3000 上运行 react。

首先,我建议将您的服务器和客户端代码保存在包含单独 package.json 文件的单独文件夹中

现在假设您在 /server/index.js 中有此代码,它直接来自 express 示例,但路由以 /api 开头,并且还将在端口 5000 上运行。这非常重要,您马上就会明白为什么。

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

app.get('/api/hello', (req, res) => res.send('Hello World!'))

app.listen(5000, () => console.log('Example app listening on port 5000!'))

现在回到您的 /client 文件夹,我将假设您的 CRA 所在的文件夹,打开 package.json 并添加以下行:

"proxy": {
  "/api/*": {
    "target": "http://localhost:5000"
  }
},

现在尝试从 react 与 axios 调用服务器,例如:

const helloFromApi = 
  axios
    .get('/api/hello')
    .then(res => res.data);

希望对你有帮助

2019 年 10 月 21 日更新

package.json 中的代理字段必须是字符串

"proxy": "http://localhost:5000"

【讨论】:

  • 我只是想知道,这样做方便吗?如果我在客户端上有与 /api/hello 相同的路由怎么办?
  • @manan5439 这是推荐的做法。由于您可以控制路由,因此很容易确保您遵守约定并避免在客户端和服务器上使用相同的路由。
  • 这是否使您的 api 不需要任何 nginx 配置? (假设 react 应用有足够的配置)
  • 5000 是标准的 http 端口,因此如果您只希望该端口上的 API 流量并通过另一个端口将流量引导到 React 应用程序,这将非常有用。大多数生产设置似乎将端口 5000 和 443 转发到 React 应用程序侦听的任何端口,因此对于 API 流量使用不同的端口号(例如 3001)可能更好。设置同上。
【解决方案3】:

但最终我相信这个问题是因为 Node 后端运行在与 React 前端不同的端口上。

好的,

MERN 很棒。 我唯一的问题是我无法在 React 端使用 Mongoose,我遇到了这个问题,几个小时后,我找到了更好的解决方案,

无需在package.json上放任何东西,无需担心CORS,

这是一个使用 mongoose 进行用户注册的工作示例(mongoose 永远不会在客户端运行,不要浪费时间,修改库很耗时),

在一个端口上启动你的 express 服务器,比如说 3030,React 在 3000 上运行,

在 React 方面

constructor(){ 
...
  this.server = server || 'https://my.ip.add.ress:3030'
...
}

register(username, password, signup = true) { 
return this.fetch(`${this.server}/server/register`, {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                username,
                password,
                signup
            })
        }).then(res => { console.log(res);
            this.setToken(res.token) // Setting the token in localStorage
            return Promise.resolve(res);
        })
}

在 Node.JS 服务器(快递)端,

创建一个文件夹'server'并创建一个文件server.js,

var MongoNode   = require('mongoosenode')   // I created this package for just to test mongoose which doesn't run on React side, 
var cors        = require('cors');  //use cors for cross-site request

var options = {
        key     : fs.readFileSync('server.key'),
        cert    : fs.readFileSync('server.cert'),
    };

    /*
     * Cors Options
     */ 
    var whitelist = config.allowedOrigins //put https://my.ip.add.ress:3000 in the allowedOrigins array in your config file
    var corsOptions = {
        origin: function (origin, callback) {
            if (whitelist.indexOf(origin) !== -1) {
                callback(null, true)
            } else {
                callback(new Error('Not allowed by CORS'))
            }
        }
    }
    //specify the port
    var https_port = config.server.port || 3030;

    //use app or any route included to server.js
     app.post('/register', cors(corsOptions),function(req, res) {
        //Process requests
        console.log(req.body);  //see if request payload popping up
        var mn = new MongoNode('mongodb://username:password@127.0.0.1:27017/databasename')

        var user = mn.retrieveModel('User','User').then(async(res) => { 
           try { 
             user = res.model;
              console.log(user);
              user.username = req.body.username
              user.password = req.body.password
              user.token = token_str  //jwt web token to save browser cookie 
              user.save(function(err) {
                 if (err) throw err;
                 console.log('user saved successfully');
                 res.json({ success: true, token: user.token});
              });                  

           }catch(e) { 
             console.log(e);
           }
        })

        user.save(function(err) {
            if (err) throw err;
            //console.log('user saved successfully');
            res.json({ success: true , message: 'user saved successfully', token : user.token });
        });

    }

瞧!阅读几个小时后很容易完成。

【讨论】:

    【解决方案4】:

    我知道这回答太晚了,但可能对寻求更多解决方案的人有所帮助。 如果您在 Docker 的帮助下创建映像,则此解决方案可以应用于具有相同端口的节点后端的 react 应用程序或 Angular 应用程序。

    因此,每当您在生产级别部署项目时。只需借助 npm run build 构建您的 Angular 或 React 项目,然后在您的 express 应用程序中借助 express static 提供整个构建文件夹。

    所以你的 Docker 文件可能是这样的

    # The builder from node image
    FROM node:8-alpine as web-app
    
    # Move our files into directory name "app"
    WORKDIR /app
    COPY package.json /app/
    RUN cd /app && npm install
    COPY . /app
    RUN cd /app && npm run build  // build your front end
    
    EXPOSE 5000
    
    CMD [ "node", "server.js" ] // start your backend
    

    这将在端口 5000 上启动后端。

    现在在 app.js 文件或任何你有你的服务器文件的地方,像这样提供构建文件夹

    app.use(express.static(path.join(__dirname, 'build')))
    

    如果您想在本地进行测试。您可以创建上面的 docker 文件并如上所示更改 app.js 以提供静态文件。然后像这样构建并启动docker镜像

    docker build . -t web-app
    docker run -p 5000:5000 web-app
    

    现在您的前端已在生产级别构建并由 express 提供服务。

    请记住,在本地,您始终可以启动两个端口进行开发,并使用 react 或 angular 提供的功能,例如在前端更改后自动重新加载,使开发变得容易。

    【讨论】:

      【解决方案5】:

      这可能就是你想要的:

      https://mty-tidjani.medium.com/deploy-nodejs-react-app-on-a-single-port-domain-54a40f1abe16

      总而言之,您可以设置 express 以将 React 应用程序作为静态内容从服务器树的子目录提供。这是我能够找到通过单个端口提供所有内容的唯一方法,但可能还有其他方法。可能这个概念是相同的,因为正如其他人所提到的,您不能让两个服务共享同一个端口。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-20
        • 2018-01-14
        • 2015-04-28
        • 2015-01-07
        • 1970-01-01
        • 2021-04-11
        • 2022-06-29
        • 1970-01-01
        相关资源
        最近更新 更多