Express 和 React Routes 如何处理来自浏览器的初始 GET 请求?

Posted

技术标签:

【中文标题】Express 和 React Routes 如何处理来自浏览器的初始 GET 请求?【英文标题】:How does Express and React Routes work on initial GET request from browser? 【发布时间】:2019-06-14 09:26:02 【问题描述】:

我是 react 世界和整个全栈世界的新手,但我一直在无休止地寻找以下问题的答案,非常感谢一些指导。

我正在使用 React 和 Express 创建一个应用程序。它需要身份验证,所以我打算使用 Passport 来提供帮助。客户端 JS 使用 React Routers 浏览网站。没关系,但我的问题是浏览器发出的初始 GET 请求。

我将首先描述我的具体应用要求,然后概括我不理解的内容。

正如我所说,我的应用程序需要 OAuth2 身份验证。如果您尝试在我的网站上获取路径并且您没有登录,它应该只加载登录页面。如果您已登录,则正常加载并找到您的路径。与 facebook 类似,我希望登录 URL 与“提要”页面相同。如此类似于 facebook.com '/' 路由是登录页面还是您的新提要,具体取决于您是否已登录,我想要同样的东西。

据我了解,Passport 通过检查请求标头在后端进行身份验证。所以我知道我应该有某种中间件,上面写着“如果用户已登录,则继续沿着路线走,否则会呈现登录页面”......这是怎么做到的?代码会是什么样子?我对 Express 的唯一体验来自一个介绍类,该类使用 res.render 发送回 html 文件并将其通过一些模板引擎(如把手)传递。但我不知道它如何与反应路线一起工作。我还会使用 res.render() 吗?还有什么?

假设我的 index.html 有根 div 来注入反应。如果我不得不猜测,我会用带有路由的 .js 文件发回那个 index.html 页面,并以某种方式在后端发回我希望它在我的反应路由上匹配的路由(登录或用户)要求)??

更一般地说,我想我只是对如何使用反应路由完成对网站的初始请求感到困惑。 1)服务器如何与一切交互以呈现我要求的内容? 2)代码会是什么样子。我对 React 的唯一体验来自 Udemy 基础课程,该课程仅使用“react-scripts start”来呈现页面。

花了一整天谷歌搜索这个问题后,它把我带到了 s-s-r,它本身就是一个兔子洞,我什至不确定它是否需要帮助我。是吗?

我显然缺少一些基础知识,因为这真的让我很困惑,所以如果您有任何资源可以了解更多信息,请发布它们。谢谢!

【问题讨论】:

【参考方案1】:

我认为您正在努力解决的根本差距源于那些“入门课程”将整个浏览器客户端推入应用程序服务器以快速启动和运行,例如,Node 服务器呈现整个 React app AND 作为 API 运行...

 // Ajax request from React app to: http://example.com/api

app.use('/api/*'),()=> 
   res.send( <!-- some JSON object -->)
)

// User visits in browser: http://example.com/**/*     

app.use('/*',()=>
   res.render(<!-- entire React App sent to browser -->)
)

第一个请求(假设用户没有访问 /api/* )只会发送 React 包。客户端内的进一步用户导航通常会将 XHR 请求(或打开的 WebSockets)从 React 应用程序发送到在同一节点程序上运行的 Express 路由。

在许多情况下,将程序的这些部分分开是有意义的,因为反应是从与请求数据的位置完全不同的位置传递的。这有很多原因,但优化计算资源以满足他们对 CPU、内存、网络等的不同需求以及代码/部署的可管理性是我的重要原因。

例如...

用户访问:http://example.com*

    nginx、Apache、“云代理”.etc 将流量定向到静态 React 包,该包没有身份验证并且永远不会与您的节点服务器联系。 如果用户之前有 Authenticate,他们将在本地存储中拥有令牌(如果您使用 JWT 进行身份验证),并且您的 React 应用将被配置为在首次加载时始终检查这些令牌。 如果用户有一个令牌,它将在后台发送一个 Ajax 请求,该令牌作为一个 Header Bearer,并将发回用户数据,然后将他们重定向到一个“经过身份验证的页面”,就像您提到的 FB 提要一样。李> 如果他们没有令牌或令牌身份验证失败,那么 React 会将他们重定向到登录或注册页面

反应

React 基本上是浏览器的原生“位置”功能(在您的域名之后显示的内容)。因此,初始页面加载后的任何事件(按钮点击等)都完全由 React 在内部处理,并使用这些路由来确定要显示的内容或通过 Ajax (XHR) 从 API 获取的数据。

如果用户执行硬页面重新加载,则该请求将返回服务器并重新执行整个循环

反应路由器

允许你同时做两件事...

    操纵浏览器Location和History对象。 通过检测更改和发送事件,在其他地方使用该历史记录和位置信息。

s-s-r

我只是玩弄了 s-s-r,所以我可以和它说话,但它为初始渲染提供了极低的延迟,在 1 个网络请求中完成,所以你想在程序的重要区域使用它。

不确定这是否能回答您的问题,但如果您希望我详细说明任何内容或提供一些更详细的资源,请告诉我。

【讨论】:

谢谢!在您的第一个代码示例中,您假设浏览器中的“入口点”是“/”路由。但它可以是我网站上的任何页面。您是否需要所有路线的 app.get() - 如果用户做了 example.com/about 关于您的第二个示例,我知道后续调用将是 AJAX 调用,因为我将使用反应路由进行内部导航。主要的混淆是通过浏览器初始呈现页面。如何在后端,如果用户要求 /about 页面,我可以将他们重定向到 '/' 登录页面 PS:我的第一个示例只是伪代码,但如果您了解 Express 路由,我意识到那会令人困惑。我已经将其更改为现在可以实际运行,例如,除了 /api/* 之外的每个请求结构都将直接定向到 React,但您也可以看到为什么这可能是低效/危险的。您的用户现在可能会意外查询您的 API。我从一个像“api.example.com”这样的子域提供了很多我的 API,所以有明确的关注点分离。 刚刚更新了我的解决方案来回答你的问题,因为我认为这是你所问问题的重要部分。 还有什么我可以帮忙的吗?如果不是,并且您发现这是最合适的答案,请介意将此答案标记为“已接受”,以便我得到漂亮的绿色复选标记:)【参考方案2】:

我理解您的挣扎,因为在将前端与后端(尤其是 React 和 Node.所以首先,我们知道浏览器/客户端总是会向服务器发起请求,那么 React Router 是如何控制路由的呢?其实它很简单,你所要做的就是从你的快递服务器的任何路线返回整个反应应用程序。代码将如下所示:

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

app.get('/*', (req, res, next) => 
  // Return React App index.html
);

app.listen(3000);

一旦 react 应用程序在用户浏览器上呈现(不要担心路径,因为 react 会根据您在客户端编写的代码根据 URL 自动呈现,它还会处理身份验证与提要页面当它扫描您的本地存储、cookie 等时,它将控制路由,而不是发送到 express 服务器的请求。但是当我们从服务器请求数据时会发生什么,它会在每个路由上返回 react app,所以我们需要设置一个 api 路由来处理任何数据请求。

app.get('/api/v1/*', (req, res, next) 
  // Return some data in json format 
);

希望这能让您深入了解您在寻找什么。

【讨论】:

谢谢!据我了解,我们只返回 index.html 页面(带有我们注入 React 的 的根页面),并且从那里反应路由将在浏览器中输入的 URL 上运行它的魔力。那是对的吗?一些后续问题:1)在您的第一个 app.get() 中,返回索引的代码是什么。 html ...是 res.render(index.html) 还是其他什么? 2) 我的代码库的设置方式是有一个用于前端文件的存储库和一个用于后端文件的存储库。 index.html 和所有 React 路由都位于前端仓库中,而 express 位于后端仓库中。 抱歉,空间不足 - 你有什么解决办法吗?如果 index.html 在不同的存储库中,如何返回它? 您好,您理解正确。此外,大多数项目都必须与您的项目一样,前端和后端彼此分开。通常在测试您的应用程序时,您在 webpack 开发服务器上运行 react 应用程序并在节点服务器上运行 express 应用程序(它们彼此独立)。在生产中情况并非如此,您的 express 应用程序必须知道客户端文件的位置,该位置由 app.use(express.static(__dirname + './..front-end/dist/')); 设置(通常 /dist 是您的前端文件内置的目录 & 包含 html 脚本 ico等文件)。 这可能会帮助您进一步澄清您的观点:medium.com/front-end-weekly/…【参考方案3】:

s-s-r 对于经验较少的开发者来说有点迷惑,暂时忘记它吧。

假设前端 javascript (React) 和后端 JavaScript (NodeJS) 是两个独立的应用程序,它们通过 API 相互通信会更容易。

此处显示Login组件和Feed组件的代码,具体取决于您是否已登录

import React,  Component  from "react";
import axios from "axios";

class Home extends Component 
  constructor() 
    const accessToken = localStorage.getItem("accessToken");
    this.state = 
      accessToken,
      feeds: []
    ;
  

  componentDidMount() 
    if (this.state.accessToken) 
      axios(`api/feeds?accessToken=$this.state.accessToken`).then(( data ) => 
        this.setState(
          feeds: data
        );
      );
    
  

  render() 
    if (this.state.accessToken) 
      return <FeedsComponent feeds=this.state.feeds />;
    
    return <LoginComponent />;
  

这是你的后端

const express = require("express");

const app = express();

app.get('/api/feeds', (req, res, ) => 
    const feeds = [
        ,
        
    ]
    res.status(200).json(feeds);
);

app.listen(3001);

请记住,它们是两个独立的应用程序,可以位于两个不同的文件夹、不同的服务器、不同的端口。

【讨论】:

以上是关于Express 和 React Routes 如何处理来自浏览器的初始 GET 请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 mocha 测试我的 express 应用程序?

如何将 this.state 转移到 react-routes?

在服务器端呈现 Express/React 应用程序上使用 passport-jwt

如何使用 React Context API 跨多个 Routes 拥有状态?

如何使用 ajax 从 react 到 express?

在React服务器端呈现中使用.dispatch和箭头函数了解链接的.map和.filter