React SPA 在刷新时找不到页面 - 从导航栏中选择时加载正常

React SPA won't find page upon refresh - loads fine when selected from navbar

我有一个在前端使用 React/Redux 编写的应用程序,在后端使用 .NET Core 2.2。这是典型的 SPA 应用程序,我相信我很久以前就开始使用 CreateReactApp。我之前已经设置了路由正常工作的页面,当我按下键盘上的 F5 或放置应该导航到页面的 URL 时,一切都会按预期加载。您将在下面的示例中看到这一点,AuditLog 和 AddLicenses 页面在刷新时或当我手动输入 URL(dev URL 是)时正确加载。

我正在我的应用程序上开发一个名为 Copy 的新功能,这个新功能应该有自己的 URL 来解析为 (http://localhost:8080/Copy)。但是,由于某种原因,当我按 F5 时 URL 不会实际加载页面,并且出现 405 错误。如果我手动将 URL 复制并粘贴到新选项卡中,这也是一样的。但是,如果我从导航栏 select 它,它加载得很好。 这是我在导航栏中 select 时的页面(我删除了一些信息,因为我不确定我可以共享多少应用程序): FromNavbar

这是我从导航栏导航到该页面后直接按 F5 时收到的错误: On F5

这是我的导航菜单的样子:

import * as React from 'react';
import { Glyphicon, Nav, Navbar, NavItem } from 'react-bootstrap';
import { Container } from 'react-bootstrap/lib/Tab';
import { LinkContainer } from 'react-router-bootstrap';
import { Link } from 'react-router-dom';
import './NavMenu.css';


export default (props: any) => (
  <Container>
  <Navbar inverse={true} fixedTop={true} fluid={true} collapseOnSelect={true}>
    <Navbar.Header>
      <Navbar.Brand>
        <Link to={'/'}>App Name</Link>
      </Navbar.Brand>
      <Navbar.Toggle />
    </Navbar.Header>
    <Navbar.Collapse>
        <Nav>
        <LinkContainer to={'/Home'}>
          <NavItem>
              <Glyphicon glyph='home' /> Home
          </NavItem>
        </LinkContainer> 
        <LinkContainer to={'/AuditLog'}>
            <NavItem>
                <Glyphicon glyph='th-list' /> Audit Log
            </NavItem>
        </LinkContainer>
        <LinkContainer to={'/Copy'}>
            <NavItem>
              <Glyphicon glyph='glyphicon glyphicon-link' /> Copy
            </NavItem>
        </LinkContainer>
        {/* <LinkContainer to={'/AddLicenses'}>
             <NavItem>
                <Glyphicon glyph='apple' /> Add Licenses
            </NavItem>
        </LinkContainer>    */}
      </Nav>
    </Navbar.Collapse>
  </Navbar>
  </Container>
);

此外,这是我的 App.tsx 文件:

import { ImplicitCallback, Security } from '@okta/okta-react';
import * as React from 'react';
import { Route } from 'react-router';
import Layout from './components/Layout';
import AddLicenses from './components/Pages/AddLicenses';
import AuditLog from './components/Pages/AuditLog';
import Copy from './components/Pages/Copy';
import Home from './components/Pages/Home';

// Todo: Move OKTA configuration to its own file -- can be shared between client and server.
export const config = {
    client_id: 'REDACTED',
    issuer: 'REDACTED',
    redirect_uri: window.location.origin + '/implicit/callback'
  }

export default () => (
    <Layout>
        <Security issuer={config.issuer}
                  client_id={config.client_id}
                  redirect_uri={config.redirect_uri}
        >
            <Route path='/' exact={true} component={Home}/>
            <Route path='/Home' exact={true} component={Home}/>
            <Route path='/AddLicenses' component={AddLicenses} />
            <Route path='/AuditLog' component={AuditLog} />
            <Route path='/Copy' component={Copy} />
            <Route path='/implicit/callback' component={ImplicitCallback}/>
        </Security>
    </Layout>
);

如果我可以提供更多信息来澄清,我将很乐意这样做。我在这个问题上停留的时间太长了,而且我是唯一开发此应用程序的开发人员,因此我们将不胜感激!

问题是这样的:

当您在 SPA 中并通过例如单击按钮导航到 http://localhost:8080/Copy 时,客户端代码会处理导航(通过弄乱浏览器的地址栏和内部历史记录)并且如您所愿。

当您直接输入 URL(或在浏览器中单击刷新)时,浏览器向 http://localhost:8080/Copy 发出请求,由您的服务器处理.回想一下刷新的作用:它指示浏览器访问地址栏中的 URL 并从服务器重新加载所有内容(可能有一些缓存)。

因此,您的 SPA 不处理刷新操作,因此无法处理路由。

@MathieuK 上面关于服务器端渲染的评论很重要,因为这是处理此类问题的主要技术:如果服务器能够响应您的 SPA 中定义的路由(通过在服务器上呈现正确的页面并将其发送到客户端),它将能够处理在您的应用程序中定义的任意路由。

另一种处理方法是在您的应用程序中处理后退或刷新按钮的单击,而不是让浏览器执行此操作。有关讨论,请参阅 this answer。然而,根据我的经验,不同的浏览器以不同的方式处理这个问题,而且这种方法可能很棘手,所以 YMMV。

我不知道为什么这个问题是以这种方式解决的,但我至少找到了解决这个问题的方法。

我知道当我在服务器上设置路由时,无论页面是否存在,这都会路由任何请求。如果该页面存在,它会将您路由到该页面。如果不存在,则默认为index.tsx。因此,我可以输入任何 URL except 副本这一事实意味着具有该特定路由的内容会导致问题。我做了一个简单的测试并更改了一个值:

<Route path='/Copy' component={Copy} />

我改成了

<Route path='/Copy2' component={Copy} />

通过更改为 /Copy2,路由现在可以正常工作了。我不知道为什么。这条路线由 reactRouter 处理,因此其中的某些内容与我的路线配合不佳。因此,我不会将其作为探索,而是继续沿用末尾带有 2 的路线。