【问题标题】:Why does my react app say "Cannot GET /route", if the user changes browsers?如果用户更改浏览器,为什么我的反应应用程序会显示“无法获取 /路由”?
【发布时间】:2020-03-31 15:44:51
【问题描述】:

我有一个使用 React-Router 和 Express 的 React 应用程序。我添加了一条新路由,它接受加密的私钥并允许用户通过指向其电子邮件收件箱的链接自动登录。

路线如下:

<PublicRoute path="/loader/:user_key" component={Loader} />

{Loader} 是一个带有 componentWillMount() 函数的 React 组件,它解析 URL,获取“user_key”,验证用户,创建 cookie,并将它们重定向到私有“/home”路由。

该链接非常有效——只要用户之前已经从相同浏览器登录到应用程序。但是,如果用户在一个浏览器(例如笔记本电脑)上创建了他们的帐户并从另一个浏览器(例如手机)打开电子邮件链接,浏览器会返回

“无法获取 /loader/...”

你有什么建议吗?我做错了什么?

编辑

这里是 server.js 文件(包含 key express 逻辑)

const bodyParser = require("body-parser");
const expressValidator = require("express-validator");
const session = require("express-session");
const MongoStore = require("connect-mongodb-session")(session);
const mongoose = require("mongoose");
const passport = require("./config/passport/index.js");
const routes = require("./routes/index.js");
const express = require("express");
const app = express();
const http = require("http");
const path = require("path");
const PORT = process.env.PORT || 3001;
const MONGODB_URI =
  process.env.MONGODB_URI || "mongodb://localhost:27017/sonar";
mongoose.Promise = Promise;

//Parse application /x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

//Server instance
const server = http.createServer(app);

//Cronjobs
const cronjob = require("./cronjob.js");

// -------------------------- Sessions -----------------------------

//Initiate sessions
app.use(
  session({
    secret: "Secret",
    store: new MongoStore({ uri: MONGODB_URI, collection: "sessions" }),
    resave: true,
    saveUninitialized: true,
    cookie: {
      maxAge: 3*60*60*1000 // In milliseconds
    }
  })
);

//Initialize passport
app.use(passport.initialize());
app.use(passport.session()); // will call the deserializeUser

// --------------------- Backend Validation---------------------------

//This validates and sanitizes strings
app.use(
  expressValidator({
    errorFormatter: (param, msg, value) => {
      const namespace = param.split("."),
        root = namespace.shift(),
        formParam = root;

      while (namespace.length) {
        formParam += "[" + namespace.shift() + "]";
      }
      return {
        param: formParam,
        msg: msg,
        value: value
      };
    }
  })
);

// -------------------------- Routes -----------------------------

//Sets static assets path
app.use(express.static(path.join(__dirname, "/build")));

//Sets route to index
app.get("/", (req, res) => {
  // console.log("Is this firing? '/' ")
  res.sendFile(__dirname, "/index.html");
});

//Setting up routes in app
app.use(routes);

// -------------------------- MongoDB -----------------------------

// Connect to the Mongo DB
mongoose.connect(MONGODB_URI, { useNewUrlParser: true }, (err, db) => {
  if (err) {
    console.log("Unable to connect to the mongoDB server. Error:", err);
  } else {
    console.log("Connection established to", MONGODB_URI);
  }
});

const db = mongoose.connection;

// Show any mongoose errors
db.on("error", error => {
  console.log("Mongoose Error: ", error);
});

// Once logged in to the db through mongoose, log a success message
db.once("open", () => {
  console.log("Mongoose connection successful.");
});

// -------------------------- Listen -----------------------------

// Start the API server
app.listen(PORT, () => {
  console.log("App listening on PORT " + PORT);
});

编辑#2

问题似乎在于加载 React-Router,一旦 server.js 返回 index.html 文件。添加index.html 文件以供参考:

 <!DOCTYPE html>

<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">

    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-132577862-1"></script>

    <!-- Google Analytics -->
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());

        gtag('config', 'UA-132577862-1');
    </script>

    <!-- This deactivates browser navigation button: "Back" -->
    <script type="text/javascript" >
       function preventBack(){window.history.forward();}
        setTimeout("preventBack()", 0);
        window.onunload=function(){null};
    </script>

    <!-- Stripe checkout -->
    <!-- <script src="https://checkout.stripe.com/checkout.js"></script> -->

    <!-- React Script -->
    <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

    <!-- <meta name="theme-color" content="#000000"> -->
    <link rel="manifest" href="manifest.json">

    <link rel="shortcut icon" href="profilePicture/infinity_preloader.gif">

    <!-- Bootstrap CDN -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" id="bootstrap-css">

    <!-- jquery -->
    <script src="//code.jquery.com/jquery-1.11.1.min.js"></script>

    <!-- Moment.js (I don't think this is necessary - can delete?) -->
    <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script> -->

    <!-- This is the IE ES6 script work around -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Array.prototype.includes"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.3.5/bluebird.min.js"></script>


    <!-- iPhone PWA icon -->
    <link rel="apple-touch-icon" sizes="180x180" href="profilePicture/dummy_profile_Pic2.png"> 

    <!-- iPhone splash screens -->
    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)" />

    <link rel="apple-touch-startup-image" href="profilePicture/dummy_profile_Pic2.png" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)" />

    <!-- Fonts -->
    <!-- <link href="https://fonts.googleapis.com/css?family=Nunito+Sans" rel="stylesheet"> -->
    <link href="https://fonts.googleapis.com/css?family=Inconsolata|Lato|Nunito|Montserrat" rel="stylesheet">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <title>Cratic | The Culture Builder</title>
  </head>
  <body>
    <div id="root" ></div>
  </body>
</html>

【问题讨论】:

  • 这里有几个问题,您的应用程序是否使用任何形式的 local storage 或其他 PWA 功能?而且,您可以发布您的主要express 应用程序文件吗?将所有请求重定向到 index.html 非常重要,这样 react-router 可以接管并呈现正确的组件。
  • 更具体地说,重要的是 react-router 和 express 一起正确运行,您可以通过 express 将 api 请求从 react 引导到处理程序,但将请求(用于 react-router)路由到索引.html - 这允许反应路由器接管并在前端显示正确的内容。
  • @MikeAbeln - 我添加了server.js 文件。我使用local storage 处理带有cookie 的私有路由。所有授权都使用passport-local 策略完成。但是,“cannot GET/”错误似乎发生在授权开始之前。

标签: node.js reactjs express npm react-router


【解决方案1】:

在您的 server.js express 文件中添加一个通配符路由,该路由捕获任何可能不匹配的路由并返回 index.html 文件,这允许 react-router 接管并在前端:

...

// -------------------------- Routes -----------------------------

//Sets static assets path
app.use(express.static(path.join(__dirname, "/build")));

//Sets route to index
app.get("/", (req, res) => {
  // console.log("Is this firing? '/' ")
  res.sendFile(__dirname, "/index.html");
});

//Setting up routes in app
app.use(routes);

// WILDCARD ROUTE HANDLER
app.get("*", (req, res) => {
  res.sendFile(__dirname, "/index.html");
};

...


这是完整的 server.js 文件:

const bodyParser = require("body-parser");
const expressValidator = require("express-validator");
const session = require("express-session");
const MongoStore = require("connect-mongodb-session")(session);
const mongoose = require("mongoose");
const passport = require("./config/passport/index.js");
const routes = require("./routes/index.js");
const express = require("express");
const app = express();
const http = require("http");
const path = require("path");
const PORT = process.env.PORT || 3001;
const MONGODB_URI =
  process.env.MONGODB_URI || "mongodb://localhost:27017/sonar";
mongoose.Promise = Promise;

//Parse application /x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

//Server instance
const server = http.createServer(app);

//Cronjobs
const cronjob = require("./cronjob.js");

// -------------------------- Sessions -----------------------------

//Initiate sessions
app.use(
  session({
    secret: "Secret",
    store: new MongoStore({ uri: MONGODB_URI, collection: "sessions" }),
    resave: true,
    saveUninitialized: true,
    cookie: {
      maxAge: 3*60*60*1000 // In milliseconds
    }
  })
);

//Initialize passport
app.use(passport.initialize());
app.use(passport.session()); // will call the deserializeUser

// --------------------- Backend Validation---------------------------

//This validates and sanitizes strings
app.use(
  expressValidator({
    errorFormatter: (param, msg, value) => {
      const namespace = param.split("."),
        root = namespace.shift(),
        formParam = root;

      while (namespace.length) {
        formParam += "[" + namespace.shift() + "]";
      }
      return {
        param: formParam,
        msg: msg,
        value: value
      };
    }
  })
);

// -------------------------- Routes -----------------------------

//Sets static assets path
app.use(express.static(path.join(__dirname, "/build")));

//Sets route to index
app.get("/", (req, res) => {
  // console.log("Is this firing? '/' ")
  res.sendFile(__dirname, "/index.html");
});

//Setting up routes in app
app.use(routes);

// WILDCARD ROUTE HANDLER
app.get("*", (req, res) => {
  res.sendFile(__dirname, "index.html");
}

// -------------------------- MongoDB -----------------------------

// Connect to the Mongo DB
mongoose.connect(MONGODB_URI, { useNewUrlParser: true }, (err, db) => {
  if (err) {
    console.log("Unable to connect to the mongoDB server. Error:", err);
  } else {
    console.log("Connection established to", MONGODB_URI);
  }
});

const db = mongoose.connection;

// Show any mongoose errors
db.on("error", error => {
  console.log("Mongoose Error: ", error);
});

// Once logged in to the db through mongoose, log a success message
db.once("open", () => {
  console.log("Mongoose connection successful.");
});

// -------------------------- Listen -----------------------------

// Start the API server
app.listen(PORT, () => {
  console.log("App listening on PORT " + PORT);
});

如果您有错误或者这不能解决问题,请告诉我!

【讨论】:

  • 好的,我得到了这个工作 - 并且 dom 正在接收 index.html 文件,但是 React 没有启动......?我在index 文件中添加了一个 React CDN,但 React 仍然无法加载。想法?
  • @IsraelPeck 它看起来不像您的index.html 文件正在调用任何自定义脚本来运行react。您需要将一些 &lt;script&gt; 标签添加到您从 CDN 导入 react 的下方的索引中 - 并将您的应用程序渲染到 &lt;div id="root"&gt; 元素中。
  • "custom script" 运行react 是什么意思?与 CDN 分开?你有例子吗?
猜你喜欢
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-12
  • 2023-04-05
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
相关资源
最近更新 更多