手动刷新或写入时,React-router网址不起作用


652

我正在使用React-router,当我单击链接按钮时,它可以正常工作,但是当我刷新网页时,它不会加载我想要的内容。

例如,我进去了localhost/joblist,一切都很好,因为我按链接到达这里。但是,如果刷新网页,则会得到:

Cannot GET /joblist

默认情况下,它不是这样工作的。最初,我的URL为localhost/#/localhost/#/joblist并且它们工作正常。但是我不喜欢这种URL,所以尝试删除它#,我写道:

Router.run(routes, Router.HistoryLocation, function (Handler) {
 React.render(<Handler/>, document.body);
});

这个问题不会发生localhost/,这个总是返回我想要的。

编辑:这个程序是单页的,所以/joblist不需要向任何服务器询问任何内容。

EDIT2:我的整个路由器。

var routes = (
    <Route name="app" path="/" handler={App}>
        <Route name="joblist" path="/joblist" handler={JobList}/>
        <DefaultRoute handler={Dashboard}/>
        <NotFoundRoute handler={NotFound}/>
    </Route>
);

Router.run(routes, Router.HistoryLocation, function (Handler) {
  React.render(<Handler/>, document.body);
});

除非您使用htaccess加载您的主要旅行页面并告诉路由器使用location.pathname,否则它将无法正常工作
查尔斯·约翰·汤普森三世

您如何删除该#符号?谢谢!
SudoPlz 2015年

4
如果您将React应用托管在S3存储桶中,则只需将错误文档设置为即可index.html。这将确保index.html无论如何都将命中。
Trevor Hutto

就我而言,它在Windows中可以正常运行,但在Linux中则无法运行
Ejaz Karim

Answers:


1093

查看有关已接受答案的评论以及此问题的一般性质(“不起作用”),我认为这可能是对此处涉及的问题进行一些一般性解释的好地方。因此,此答案旨在作为OP的特定用例的背景信息/阐述。请多多包涵。

服务器端与客户端

首先要了解的是,现在有2个地方可以解释URL,而在“过去”中,以前只有1个地方。过去,当生活简单时,一些用户发送了一个请求http://example.com/about服务器服务器检查了URL的路径部分,确定该用户正在请求About页面,然后将该页面发回。

使用客户端路由(这是React-Router提供的功能),事情就变得不那么简单了。首先,客户端尚未加载任何JS代码。因此,第一个请求将始终是服务器。然后,这将返回一个页面,其中包含用于加载React和React Router等所需的脚本标记。仅在加载了这些脚本后,阶段2才会启动。在阶段2中,例如,当用户单击“关于我们”导航链接时,URL 本地更改为http://example.com/about(通过History API可以更改),但未向服务器发出请求。相反,React Router在客户端执行其操作,确定要渲染的React视图并进行渲染。假设您的About页面不需要进行任何REST调用,那么它已经完成了。您已从首页转换为关于我们,而没有触发任何服务器请求。

因此,基本上,当您单击链接时,将运行一些Javascript来操纵地址栏中的URL,而不会导致页面刷新,这又导致React Router 在客户端执行页面转换。

但是,现在考虑如果将URL复制粘贴到地址栏中并将其通过电子邮件发送给朋友,会发生什么情况。您的朋友尚未加载您的网站。换句话说,她仍处于阶段1。她的机器上还没有运行React Router。所以,她的浏览器将使服务器的请求,以http://example.com/about

这就是您麻烦的开始。到现在为止,您只需将静态HTML放置在服务器的webroot上就可以摆脱困境。但这会在从服务器请求时404为所有其他URL 提供错误。这些相同的URL 在客户端可以正常工作,因为那里的React Router正在为您进行路由,但是除非您使服务器理解它们,否则它们在服务器端将失败。

结合服务器端和客户端路由

如果希望http://example.com/aboutURL在服务器端和客户端上都可以使用,则需要在服务器端和客户端上为其设置路由。有道理吧?

这就是您开始选择的地方。解决方案的范围从完全绕过问题(通过返回引导HTML的包罗万象的路线)到完全同构的方法(在该方法中,服务器和客户端都运行相同的JS代码)。

完全绕开该问题:哈希历史记录

使用哈希历史记录而不是浏览器历史记录,“关于”页面的URL看起来像这样: http://example.com/#/about 哈希(#)符号后面的部分不会发送到服务器。因此,服务器仅http://example.com/按预期方式看到并发送索引页。React-Router将拾取#/about零件并显示正确的页面。

缺点

  • “丑陋”的网址
  • 使用这种方法无法进行服务器端渲染。就搜索引擎优化(SEO)而言,您的网站只包含一个页面,几乎没有任何内容。

包罗万象

通过这种方法,您确实使用了浏览器历史记录,但是只是在发送/*index.html,有效地为您提供了与哈希历史记录相同的情况。但是,您确实拥有干净的URL,以后可以改进此方案而不必使所有用户的收藏夹均无效。

缺点

  • 设置更复杂
  • 仍然没有好的SEO

杂种

在混合方法中,您可以通过为特定路由添加特定脚本来扩展包罗万象的方案。您可以制作一些简单的PHP脚本来返回包含内容的网站最重要页面,以便Googlebot至少可以看到页面上的内容。

缺点

  • 设置起来更加复杂
  • 对于那些给予特殊待遇的路线,只有好的SEO
  • 复制代码以在服务器和客户端上呈现内容

同构

如果我们使用Node JS作为服务器,以便我们可以在两端运行相同的 JS代码怎么办?现在,我们在单个react-router配置中定义了所有路由,并且不需要复制渲染代码。可以这么说,这是“圣杯”。如果客户端发生页面转换,服务器将发送与最终相同的标记。就SEO而言,此解决方案是最佳的。

缺点

  • 服务器必须(能够)运行JS。我已经尝试过Java icw Nashorn,但是它不适用于我。实际上,这主要意味着您必须使用基于Node JS的服务器。
  • 许多棘手的环境问题(window在服务器端使用)
  • 陡峭的学习曲线

我应该使用哪个?

选择一个您可以摆脱的。就我个人而言,我认为综合起来很简单,所以这是我的最低要求。通过此设置,您可以随着时间的流逝而改进。如果您已经使用Node JS作为服务器平台,那么我肯定会研究使用同构应用程序。是的,一开始很难,但是一旦掌握了它,它实际上是解决问题的一种非常优雅的方法。

因此,对我而言,基本上,这就是决定性因素。如果我的服务器在Node JS上运行,我将同构。否则,我会选择全包解决方案,并随着时间的推移和SEO要求对其进行扩展(混合解决方案)。

如果您想了解更多有关使用React进行同构(也称为“通用”)渲染的知识,那么有一些关于该主题的不错的教程:

另外,为使您入门,我建议您看一些入门套件。选择一个与您选择的技术相匹配的技术(请记住,React只是MVC中的V,您需要更多的东西来构建完整的应用程序)。首先看一下Facebook本身发布的内容:

或从社区中选择许多。现在有一个不错的站点尝试索引所有这些站点:

我从这些开始:

目前,我使用的是通用的自制版本,该版本受上述两个入门工具包的启发,但现在已经过时了。

祝您一切顺利!


2
伟大的帖子Stijn!您是否建议使用同构React应用程序的入门套件?如果是这样,您能举一个您喜欢的例子吗?
克里斯

1
@ Paulos3000这取决于您使用的服务器。基本上,您可以为/*它定义一条路由,并使其响应HTML页面。这里的棘手事情是确保您不使用此路由拦截对.js和.css文件的请求。
Stijn de Witt

2
@ Paulos3000有关一些相关问题,请参见此处:Apache / phpExpress / jsJ2E / Java
Stijn de Witt

1
嗨,我正在尝试哈希解决方案,但是没有运气。遇到createMemoryHistory is not a function错误。介意一下吗?stackoverflow.com/questions/45002573/...
莱昂Gaban

5
@LeonGaban看来自从写了这个答案以来,React Router改变了它们的实现。现在,它们针对不同的历史记录具有不同的Router实例,并且它们在后台进行历史记录的配置。基本原理仍然相同。
Stijn de Witt

115

这里的答案都是非常有用的,对我有用的是配置Webpack服务器以期望路由。

devServer: {
   historyApiFallback: true,
   contentBase: './',
   hot: true
},

historyApiFallback是为我解决此问题的原因。现在,路由可以正常工作,我可以刷新页面或直接输入URL。无需担心节点服务器上的变通方法。该答案显然仅在使用webpack时有效。

编辑:这里是我的答案,这是为什么有必要的更详细的原因:https : //stackoverflow.com/a/37622953/5217568


16
请注意,Webpack团队建议不要在生产环境使用开发服务器。
Stijn de Witt

5
对于通用开发目的,这是最佳解决方案。historyApiFallback足够了。至于所有其他选项,也可以在CLI中使用标志进行设置--history-api-fallback
Marco Lazzeri,2016年

2
@Kunok不是。这是一个快速的开发解决方案,但是您仍然需要为生产解决一些问题。
Stijn de Witt

contentBase:':/'因为可以从URL访问您的应用文件
Nezih

85

您可以更改.htaccess文件并插入以下内容:

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-l
  RewriteRule . /index.html [L]
</IfModule>

我正在使用react: "^16.12.0"并且react-router: "^5.1.2" 此方法是万能的,可能是入门的最简单方法。


3
这样很好!最简单的方法是不想重组应用程序
mhyassin

7
不要忘记RewriteEngine On作为第一行
Kai Qing

3
请注意,此答案是指Apache服务器上的config。这不是React应用程序的一部分。
科尔顿·希克斯

我不明白上面的答案。没有一个老师说我的链接根本无法正常工作。但是,此简单的htaccess文件有效。谢谢
Thomas Williams

这也适用于next.js静态导出。由于tomcat与节点不相似,因此我们需要对next.js静态导出进行此额外配置。
giri-jeedigunta

62

对于React Router V4用户:

如果您尝试通过其他答案中提到的哈希历史记录技术来解决此问题,请注意,

<Router history={hashHistory} >

在V4中不起作用,请HashRouter改用:

import { HashRouter } from 'react-router-dom'

<HashRouter>
  <App/>
</HashRouter>

参考:HashRouter


您能否详细说明为什么HashRouter可以解决此问题?您提供的链接无法为我解释。另外,有没有办法在路径中隐藏哈希?我用的是BrowserRouter但遇到了这个问题,404与它..
伊恩·史密斯

29

可以通过两种不同的方式调用路由器,具体取决于导航是在客户端还是在服务器上进行。您已将其配置为用于客户端操作。关键参数是运行方法的第二个参数,即位置。

当您使用React Router Link组件时,它会阻止浏览器导航并调用transitionTo进行客户端导航。您使用的是HistoryLocation,因此它使用HTML5历史记录API通过模拟地址栏中的新URL来完成导航的效果。如果您使用的是较旧的浏览器,则无法使用。您将需要使用HashLocation组件。

当您点击刷新时,您将绕过所有React和React Router代码。服务器收到请求,/joblist并且它必须返回某些内容。在服务器上,您需要将请求的路径传递给run方法,以使其呈现正确的视图。您可以使用相同的路线图,但可能需要对进行不同的调用Router.run。正如Charles所指出的,您可以使用URL重写来处理此问题。另一个选择是使用node.js服务器来处理所有请求,并将路径值作为location参数传递。

例如,在快递中,它可能看起来像这样:

var app = express();

app.get('*', function (req, res) { // This wildcard method handles all requests

    Router.run(routes, req.path, function (Handler, state) {
        var element = React.createElement(Handler);
        var html = React.renderToString(element);
        res.render('main', { content: html });
    });
});

请注意,请求路径正在传递到run。为此,您需要有一个服务器端视图引擎,您可以将呈现的HTML传递到该引擎。renderToString在服务器上使用和运行React时,还有许多其他注意事项。页面在服务器上呈现后,当您的应用加载到客户端时,它将再次呈现,并根据需要更新服务器端呈现的HTML。


2
请问,您能否解释下一条语句“当您点击刷新时,绕过所有的React和React Router代码”吗?为什么会发生?
VB_

通常,React路由器仅用于处理浏览器中的不同“路径”。有两种典型的方法:较旧的样式哈希路径和较新的历史记录API。浏览器刷新将发出服务器请求,该请求将绕过您的客户端响应路由器代码。您将需要处理服务器上的路径(但仅当您使用历史记录API时)。您可以为此使用React Router,但是您需要执行与上述操作类似的操作。
2015年

得到它了。但是,如果服务器使用非JS语言(例如Java)怎么办?
VB_

2
抱歉……您的意思是您需要快递服务器来呈现“非根”路由?首先让我惊讶的是,同构的定义没有在其概念中包括数据获取(如果您想在数据服务器端呈现视图,则事情非常复杂,尽管这显然是SEO的需求-同构声称如此)。现在,我就像是“ WTF反应”,认真地……如果我错了,请纠正我,ember / angular / backbone是否需要服务器来渲染路由?我真的不明白使用路线的这种过高要求
2015年

1
这是否意味着如果我的react应用程序不是同构的,则react-router中的刷新不起作用吗?
马特2015年

28

在index.html中head,添加以下内容:

<base href="/">
<!-- This must come before the css and javascripts -->

然后,当与webpack dev服务器一起运行时,请使用此命令。

webpack-dev-server --mode development --hot --inline --content-base=dist --history-api-fallback

--history-api-fallback 是重要的部分


这很棒!是否有任何链接以阅读更多有关如何获得/找到该解决方案的信息?
justDan

1
兄弟抱歉。通过反复试验的可靠技术找到了它。
Efe Ariaroo

值得一提的是,如果您使用的基础href无效,/那么HashRouter它将不起作用(如果您使用的是哈希路由)
Robbie Averill

<base href =“ /”>标记对我有用。谢谢你 Webpack开发服务器一直尝试从path / <bundle>抓取捆绑软件,但失败了。
马里斯巴德'19

27

我刚才使用create-react-app制作了一个网站,并在此处遇到了同样的问题。我BrowserRoutingreact-router-dom包装中使用。我在Nginx服务器上运行,对我来说解决了什么是将以下内容添加到/etc/nginx/yourconfig.conf

location / {
  if (!-e $request_filename){
    rewrite ^(.*)$ /index.html break;
  }
}

这对应于.htaccess在运行Appache的情况下将以下内容添加到

Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]

这似乎也是Facebook自己建议的解决方案,可以在这里找到


1
哇,nginx的简单解决方案;作为新的Nginx用户,其工作原理很像魅力。只需复制粘贴即可。+1用于链接到官方fb文档。要走的路。
user3773048

作为记录:我正在将AWS Instance与nginx和Angular App一起使用。这可行。
Diego Sarmiento

你救我。从昨天开始,我正试图解决我的问题,我累了几个解决方案,但我失败了。最终您帮助了我,您解决了这个路由器问题。嗯,非常感谢兄弟
Sharifur Robin

“ react”:“ ^ 16.8.6”,“ react-router”:“ ^ 5.0.1”是否有其他不同?我尝试了这些解决方案(nginx和apache),但它们不起作用。
Mark A. Tagliaferro

由于Facebook尚未更改有关此文档的文档,因此我只能假设过程是相同的。我已经有一段时间没有尝试过了。
艾丁

17

这样可以解决你的问题

在生产模式下的ReactJS应用程序中,我也遇到了同样的问题。这是该问题的2个解决方案。

1.将路由历史更改为“ hashHistory”,而不是用browserHistory

<Router history={hashHistory} >
   <Route path="/home" component={Home} />
   <Route path="/aboutus" component={AboutUs} />
</Router>

现在使用命令构建应用

sudo npm run build

然后将build文件夹放在您的var / www /文件夹中,现在该应用程序可以正常工作,在每个URL中添加#标签。喜欢

本地主机/#/ home本地主机/#/ aboutus

解决方案2:不使用浏览器历史记录#标签,

在路由器中设置您的历史= {browserHistory},现在使用sudo npm run build对其进行构建。

您需要创建“ conf”文件来解决“ 404找不到页面”,conf文件应如下所示。

打开您的终端,键入以下命令

cd / etc / apache2 / sites-available ls nano sample.conf在其中添加以下内容。

<VirtualHost *:80>
    ServerAdmin admin@0.0.0.0
    ServerName 0.0.0.0
    ServerAlias 0.0.0.0
    DocumentRoot /var/www/html/

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
    <Directory "/var/www/html/">
            Options Indexes FollowSymLinks
            AllowOverride all
            Require all granted
    </Directory>
</VirtualHost>

现在,您需要使用以下命令来启用sample.conf文件

cd /etc/apache2/sites-available
sudo a2ensite sample.conf

然后它将要求您使用sudo服务apache2重新加载或重新启动apache服务器

然后打开您的localhost / build文件夹并添加内容如下的.htaccess文件。

   RewriteEngine On
   RewriteBase /
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_FILENAME} !-l
   RewriteRule ^.*$ / [L,QSA]

现在,该应用程序可以正常运行。

注意:将0.0.0.0 ip更改为您的本地IP地址。

如果对此有任何疑问,请随时提出评论。

希望对其他人有帮助。


第一个解决方案适用于"react-router-dom": "^5.1.2"吗?,我正在使用<BrowserRouter>
151291

16

如果您通过AWS Static S3 Hosting和CloudFront托管React应用

CloudFront响应403 Access Denied消息后就出现了此问题,因为它希望/ some / other / path存在于我的S3文件夹中,但是该路径仅存在于React的使用react-router的路由内部。

解决方案是设置分发错误页面规则。转到CloudFront设置,然后选择您的分配。接下来转到“错误页面”标签。单击“创建自定义错误响应”并添加403的条目,因为这是我们得到的错误状态代码。将“响应页面路径”设置为/index.html并将状态码设置为200。最终结果以其简单性令我惊讶。提供了索引页面,但URL保留在浏览器中,因此,一旦React应用加载,它将检测URL路径并导航到所需的路由。

错误页面403规则


1
这正是我所需要的。谢谢。
乔纳森

所以..您所有的网址都可以,但是返回状态码403?这是不正确的。预期的状态代码应在2xx范围内。
Stijn de Witt

@StijndeWitt我不确定您是否正确理解。CloudFront将处理所有内容-客户端将看不到任何403状态代码。重新路由发生在服务器端,呈现索引页,维护URL,并提供正确的路由。
th3morg19年

@ th3morg啊,是的,我现在明白了。我错过了它还允许您将响应状态代码填充为200。因此,基本上,您正在将状态403映射到状态200 ...看起来还不错...除了可能由于其他原因而得到403因此,您将无法看到它。
Stijn de Witt

1
感谢这个@ th3morg。此设置是否也适用于多级路径?如果在根级别上有任何路径(例如domain / signup,domain / login,domain / <documentId>),它对我来说非常有用,但是对于诸如domain / document / <documentId>这样的多级路径则不起作用。
Pargles

15

Webpack Dev Server具有启用此功能的选项。打开package.json并添加--history-api-fallback。该解决方案为我工作。

反应路由器教程


3
很好,webpack-dev-server但是如何为生产版本修复此问题?
侯赛因

12

如果您在IIS上托管react应用,只需添加一个web.config文件,其中包含:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404" subStatusCode="-1" />
        <error statusCode="404" path="/" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

这将告诉IIS服务器将主页返回到客户端而不是404错误,并且不需要使用哈希历史记录。


多谢兄弟!!!!这是工作!
清宝

12

将此添加到webpack.config.js

devServer: {
    historyApiFallback: true
}

这对开发人员来说很棒,但对生产构建没有帮助。
KFunk

11

生产堆栈:React,React Router v4,BrowswerRouter,Express,Nginx

1)用户BrowserRouter提供漂亮的网址

// app.js

import { BrowserRouter as Router } from 'react-router-dom'

const App = () {
  render() {
    return (
        <Router>
           // your routes here
        </Router>
    )
  }
}

2)使用将index.html添加到所有未知请求 /*

// server.js

app.get('/*', function(req, res) {   
  res.sendFile(path.join(__dirname, 'path/to/your/index.html'), function(err) {
    if (err) {
      res.status(500).send(err)
    }
  })
})

3)捆绑webpack与 webpack -p

4)运行nodemon server.jsnode server.js

编辑:您可能想让Nginx在服务器块中处理此问题,而忽略步骤2:

location / {
    try_files $uri /index.html;
}

获取不确定的路径
Goutham

@Goutham在server.js文件的顶部添加import path from 'pathconst path = require('path')
Isaac Pak

第2步(使用捕获所有内容/*)刚刚保存了我的一天。谢谢!:)
Atlas7年7

对于使用Redux并关心连接路由器的用户,此方法有效,请参见以下示例:github.com/supasate/connected-react-router/tree/master/examples/…–
cjjenkinson

10

如果您使用的是Create React App:

有一个伟大的步行路程,虽然这个问题与许多大型主机平台的解决方案,你可以找到这里的创建做出反应应用页面上。例如,我将React Router v4和Netlify用作前端代码。只需要在我的公用文件夹中添加一个文件(“ _redirects”),然后在该文件中添加一行代码即可:

/*  /index.html  200

现在,当输入浏览器或有人刷新时,我的网站可以正确呈现诸如mysite.com/pricing之类的路径。


7

尝试使用以下代码在公用文件夹内添加“ .htaccess”文件。

RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

RewriteRule ^ /index.html [L]  

谢谢,这就是使用Apache在Fedora 28上为我工作的原因。以上重写规则或CRA页面上的规则均不适用于我。我将它们添加到虚拟主机设置中,而不是添加到单独的.htaccess文件中。
boerre

6

如果确实有index.html的备用版本,请确保在index.html文件中具有以下内容:

<script>
  System.config({ baseURL: '/' });
</script>

这可能因项目而异。


2
将此添加到您的html中head:<base href =“ /”>
Efe Ariaroo

4

如果您使用的是Firebase,则只需确保在应用程序根目录(在托管部分中)的firebase.json文件中具有rewrites属性。

例如:

{ 
  "hosting": {
    "rewrites": [{
      "source":"**",
      "destination": "/index.html"
    }]    
  }
}

希望这可以节省别人的挫折感和浪费的时间。

祝您编程愉快!

关于该主题的进一步阅读:

https://firebase.google.com/docs/hosting/full-config#rewrites

Firebase CLI:“配置为单页应用程序(将所有URL重写为/index.html)”


您是一个传奇人物
Perniferous


3

我还没有使用服务器端渲染,但是遇到了与OP相同的问题,在大多数情况下,Link似乎都可以正常工作,但是当我有参数时却失败了。我将在此处记录我的解决方案,以查看它是否对任何人都有帮助。

我的主要jsx包含以下内容:

<Route onEnter={requireLogin} path="detail/:id" component={ModelDetail} />

对于第一个匹配的链接,这很好用,但是当:id改变时 <Link>嵌套在该模型的详细信息页面上的表达式时,浏览器栏中的url会更改,但是该页面的内容最初并未更改以反映链接的模型。

麻烦的是我曾用来props.params.id设置模型componentDidMount。该组件仅安装一次,因此这意味着第一个模型是粘贴在页面上的模型,随后的Links更改了道具,但页面看起来没有变化。

将模型同时设置为组件状态componentDidMountcomponentWillReceiveProps基于组件的状态(此处基于下一个道具)将解决该问题,并且页面内容会更改以反映所需的模型。


1
如果您想尝试服务器端渲染,最好使用组件构造函数(也可以访问props)iso componentDidMount。因为componentDidMount仅在浏览器中调用。目的是对DOM进行处理,例如将事件侦听器附加到body,而不能在中执行render
Stijn de Witt

3

这个主题有点老了,已经解决了,但是我想向您推荐一个简单,清晰和更好的解决方案。如果您使用Web服务器,则可以使用。

每个Web服务器都具有在http 404的情况下将用户重定向到错误页面的功能。要解决此问题,您需要将用户重定向到索引页面。

如果使用Java基本服务器(tomcat或任何java应用程序服务器),则解决方案可能是以下几种:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!-- WELCOME FILE LIST -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- ERROR PAGES DEFINITION -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.jsp</location>
    </error-page>

</web-app>

例:

  • GET http://example.com/about
  • Web服务器抛出http 404,因为此页面在服务器端不存在
  • 错误页面配置告诉服务器将index.jsp页面发送回用户
  • 那么JS将在客户端上完成剩下的工作,因为客户端的网址仍然是http://example.com/about

就是这样,不再需要魔术:)


太棒了!我使用的是Wildfly 10.1服务器,并将此更新更新到了web.xml文件中,但我的位置设置为仅“ /”。这是因为在我的React代码中,我使用了如下浏览器历史记录:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
您确定这不会影响SEO吗?您正在为实际存在的页面提供404状态。用户可能永远不会意识到这一点,但实际上,机器人确实引起了很多关注,以至于它们不会抓取您的页面。
subharb

3

如果您在后端使用Express或其他框架,则可以添加如下所示的配置,并在配置中签出Webpack公用路径,如果您使用BrowserRouter,即使重新加载它也可以正常工作

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});

这是最简单的解决方案。注意这条路应该去后,任何其他途径,因为它是一个包罗万象的
dcsan

3

修复刷新或直接调用URL时出现的“无法获取/ URL”错误。

配置您的webpack.config.js以使给定的链接像这样路由。

module.exports = {
  entry: './app/index.js',
  output: {
       path: path.join(__dirname, '/bundle'),
       filename: 'index_bundle.js',
       publicPath: '/'
  },

2

当我使用.Net Core MVC时,类似以下内容对我有所帮助:

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var url = Request.Path + Request.QueryString;
            return App(url);
        }

        [Route("App")]
        public IActionResult App(string url)
        {
            return View("/wwwroot/app/build/index.html");
        }
   }

基本上在MVC端,所有不匹配的路由都将落入Home/Index所指定的位置startup.cs。在内部Index可以获取原始请求url并将其传递到任何需要的地方。

startup.cs

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new { controller = "Home", action = "Index" });
        });

如果网络api与应用程序分开,您将如何处理?
dayanrr91 '19

@ dayanrr91如果您的项目具有Web api,则无论如何都不需要服务器端路由。我相信您的react-router应该阅读url并进行适当的路由。没有什么可以阻止它
Elnoor

非常感谢您的评论,但是我有一个问题,我刚刚添加了HashRouter,但是现在当我手动输入任何URL时,它将重定向到home组件,而不是重定向到home,然后再重定向到我尝试的组件进入,有什么解决方法吗?
dayanrr91 '19

@ dayanrr91真的不知道,还没有尝试过hashrouter,但是我认为应该与您的代码有关。哈希路由器的工作方式应类似于浏览器路由器。我也刚刚在此沙箱中测试过,它可以正常工作codeandbox.io/s/25okp1mny,测试网址25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

如果您在IIS中托管;将此添加到我的webconfig中解决了我的问题

<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
    <remove statusCode="500" subStatusCode="100" />
    <remove statusCode="500" subStatusCode="-1" />
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" path="/" responseMode="ExecuteURL" />
    <error statusCode="500" prefixLanguageFilePath="" path="/error_500.asp" responseMode="ExecuteURL" />
    <error statusCode="500" subStatusCode="100" path="/error_500.asp" responseMode="ExecuteURL" />
</httpErrors>

您可以对其他任何服务器进行类似的配置


1

如果有任何人在这里寻找使用Laravel在React JS SPA上的解决方案。 公认的答案是为什么会发生此类问题的最好解释。如前所述,您必须同时配置客户端和服务器端。在刀片模板中,包括js捆绑文件,请确保URL facade像这样使用

<script src="{{ URL::to('js/user/spa.js') }}"></script>

在您的路由中,确保将其添加到刀片模板所在的主要端点。例如,

Route::get('/setting-alerts', function () {
   return view('user.set-alerts');
});

以上是刀片模板的主要端点。现在也添加一条可选路线,

Route::get('/setting-alerts/{spa?}', function () {
  return view('user.set-alerts');
});

发生的问题是先加载刀片模板,然后再加载反应路由器。因此,当您加载时'/setting-alerts',它会加载html和js。但是,当您加载时'/setting-alerts/about',它将首先在服务器端加载。由于在服务器端,此位置上没有任何内容,因此找不到该返回值。当您拥有该可选路由器时,它将加载该页面,并且还加载了react路由器,然后react loader确定要显示的组件。希望这可以帮助。


是否可以加载到服务器的一个确切的URI,然后服务器重定向到React?像示例一样,当我加载时/user/{userID},我只需要返回一个通用的/userHTML视图,携带ID并使用AJAX或axios对其进行调用。有可能这样做吗?他们对SEO友好吗?
Ryuujo

1

对于那些正在使用IIS 10的用户,这是您应该采取的正确操作。确保与此同时使用browserHistory。作为参考,我将给出用于路由的代码,但这并不重要,重要的是下面的组件代码之后的下一步:

class App extends Component {
    render() {
        return (
            <Router history={browserHistory}>
                <div>
                    <Root>
                        <Switch>
                            <Route exact path={"/"} component={Home} />    
                            <Route path={"/home"} component={Home} />
                            <Route path={"/createnewproject"} component={CreateNewProject} />
                            <Route path={"/projects"} component={Projects} />
                            <Route path="*" component={NotFoundRoute} />
                        </Switch>
                    </Root>
                </div>
            </Router>
        )
    }
}
render (<App />, window.document.getElementById("app"));

由于问题是IIS接收到来自客户端浏览器的请求,因此它将像请求页面一样解释URL,然后由于没有可用的页面而返回404页面。请执行下列操作:

  1. 打开IIS
  2. 展开服务器,然后打开站点文件夹
  3. 点击网站/应用程序
  4. 转到错误页面
  5. 打开列表中的404错误状态项
  6. 代替选项“将内容从静态文件插入错误响应中”,将其更改为“在此站点上执行URL”,然后在URL中添加“ /”斜杠值。

现在它将正常工作。

在此处输入图片说明 在此处输入图片说明

希望对您有所帮助。:-)


1

我正在使用WebPack,遇到同样的问题Solution =>在您的server.js文件中

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

app.use(express.static(path.resolve(__dirname, '../dist')));
  app.get('*', function (req, res) {
    res.sendFile(path.resolve(__dirname, '../dist/index.html'));
    // res.end();
  });

为什么我的应用程序刷新后不呈现?


这为我做到了!最初我有res.sendFile(path.JOIN(publicPath,“ index.html”)); 像上面的示例一样,我将“ join”更改为“ resolved”:res.sendFile(path.resolve(“ ./ dist”,“ index.html”)); 我也在玩__dirname,但无法真正理解它或无法使其正常工作,所以我手动将其复制到“ ./dist”中,因为这是我的index.html的发源地。我也像之前这样声明过:app.use(express.static(“ ./ dist”));
logixplayer

1

使用ReduxHashRouter对我有用,只需替换:

import {
  Router //replace Router
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <Router history={history}> //replace here saying Router
            <Layout/>
        </Router>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

至:

import {
  HashRouter //replaced with HashRouter
} from "react-router-dom";

ReactDOM.render(
    <LocaleProvider locale={enUS}>
    <Provider store={Store}>
        <HashRouter history={history}> //replaced with HashRouter
            <Layout/>
        </HashRouter>
    </Provider>
</LocaleProvider>, document.getElementById("app"));
registerServiceWorker();

0

我有同样的问题,这个解决方案对我们有用。

背景:

我们在同一台服务器上托管多个应用。当我们刷新服务器时,服务器将无法理解该特定应用程序在dist文件夹中的索引位置。上面的链接将带您进入为我们服务的范围...希望对您有所帮助,因为我们已经花费了很多时间来为我们的需求找到解决方案。

我们正在使用:

package.json

"dependencies": {
"babel-polyfill": "^6.23.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"prop-types": "^15.5.6",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-persist": "^4.6.0",
"redux-thunk": "^2.2.0",
"webpack": "^2.4.1"
}

我的webpack.config.js

webpack.config.js

/* eslint-disable */
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const babelPolyfill = require('babel-polyfill');
const HTMLWebpackPluginConfig = new HtmlWebpackPlugin({
  template: __dirname + '/app/views/index.html',
  filename: 'index.html',
  inject: 'body'
});

module.exports = {
  entry: [
    'babel-polyfill', './app/index.js'
  ],
  output: {
    path: __dirname + '/dist/your_app_name_here',
    filename: 'index_bundle.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'babel-loader',
      query : {
          presets : ["env", "react", "stage-1"]
      },
      exclude: /node_modules/
    }]
  },
  plugins: [HTMLWebpackPluginConfig]
}

我的index.js

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Routes from './Routes'
import { Provider } from 'react-redux'
import { createHistory } from 'history'
import { useRouterHistory } from 'react-router'
import configureStore from './store/configureStore'
import { syncHistoryWithStore } from 'react-router-redux'
import { persistStore } from 'redux-persist'

const store = configureStore();

const browserHistory = useRouterHistory(createHistory) ({
  basename: '/your_app_name_here'
})
const history = syncHistoryWithStore(browserHistory, store)

persistStore(store, {blacklist: ['routing']}, () => {
  console.log('rehydration complete')
})
// persistStore(store).purge()


ReactDOM.render(
    <Provider store={store}>
      <div>
        <Routes history={history} />
      </div>
    </Provider>,
  document.getElementById('mount')
)

我的app.js

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

app.use(express.static(__dirname + '/dist'));
// app.use(express.static(__dirname + '/app/assets'));
app.set('views', __dirname + '/dist/your_app_name_here');
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');

app.get('/*', function (req, res) {
    res.render('index');
});

app.listen(8081, function () {
  console.log('MD listening on port 8081!');
});

0

我喜欢这种处理方式。尝试在服务器端添加: yourSPAPageRoute / *来解决此问题。

我之所以采用这种方法,是因为即使本地HTML5历史记录API也不支持页面刷新时进行正确的重定向(据我所知)。

注意:选定的答案已经解决了这个问题,但我正在尝试更具体。

快速路线

测试-历史记录API 经过测试,只是想分享。

希望能帮助到你。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.