URL-адреса React-маршрутизатора не работают при обновлении или записи вручную


655

Я использую React-роутер, и он отлично работает, пока я нажимаю на кнопки ссылок, но когда я обновляю свою веб-страницу, она не загружает то, что я хочу.

Например, я нахожусь, 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, он не будет работать.
Чарльз Джон Томпсон III,

Как вы стерли этот #символ? Спасибо!
SudoPlz

4
Если вы размещаете ваше приложение реагирования в корзине S3, вы можете просто установить документ об ошибке в index.html. Это гарантирует, что index.htmlударил, несмотря ни на что.
Тревор Хатто

В моем случае он отлично работает в Windows, но не в Linux
Ejaz Karim

Это ссылка, которая помогла решить мою проблему: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

Ответы:


1096

Глядя на комментарии к принятому ответу и общий характер этого вопроса («не работает»), я подумал, что это может быть хорошим местом для некоторых общих объяснений по вопросам, связанным с этим. Таким образом, этот ответ предназначен в качестве справочной информации / разработки по конкретному варианту использования ОП. Пожалуйста, потерпите меня.

Сторона сервера против стороны клиента

Первая важная вещь, которую нужно понять, это то, что теперь есть 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-код в корне вашего сервера. Но это приведет к 404ошибкам для всех других URL-адресов при запросе с сервера . Те же URL-адреса прекрасно работают на стороне клиента , потому что там React Router выполняет маршрутизацию за вас, но они терпят неудачу на стороне сервера, если вы не заставите свой сервер их понять.

Объединение серверной и клиентской маршрутизации

Если вы хотите, чтобы http://example.com/aboutURL работал как на стороне сервера, так и на стороне клиента, вам необходимо настроить для него маршруты как на стороне сервера, так и на стороне клиента. Имеет смысл правильно?

И вот тут начинается ваш выбор. Решения варьируются от полного обхода проблемы через универсальный маршрут, который возвращает загрузочный HTML, до полного изоморфного подхода, когда и сервер, и клиент запускают один и тот же код JS.

,

Обход проблемы в целом: Hash History

Если вместо истории браузеров использовать историюhttp://example.com/#/about хэшей , ваш URL-адрес страницы about будет выглядеть примерно так: Часть после #символа hash ( ) не отправляется на сервер. Таким образом, сервер только видит http://example.com/и отправляет страницу индекса, как и ожидалось. React-Router подберет#/about деталь и покажет правильную страницу.

Недостатки :

  • "уродливые" URL
  • При таком подходе рендеринг на стороне сервера невозможен. Что касается поисковой оптимизации (SEO), ваш сайт состоит из одной страницы, на которой почти нет контента.

,

Вместилище разнообразных предметов

При таком подходе вы используете историю браузера, а просто настроить улов-все на сервере , который посылает /*к index.html, фактически давая вам много такую же ситуацию , как с историей Hash. Тем не менее, у вас есть чистые URL-адреса, и вы могли бы улучшить эту схему позже, не делая недействительными все избранное вашего пользователя.

Недостатки :

  • Более сложная настройка
  • Все еще нет хорошего SEO

,

Гибридный

В гибридном подходе вы расширяете общий сценарий, добавляя конкретные сценарии для определенных маршрутов. Вы можете сделать несколько простых сценариев PHP, чтобы вернуть наиболее важные страницы вашего сайта с включенным содержимым, чтобы робот Googlebot мог хотя бы увидеть, что находится на вашей странице.

Недостатки :

  • Еще сложнее настроить
  • Только хороший SEO для тех маршрутов, которые вы предоставляете специальную обработку
  • Дублирование кода для рендеринга контента на сервере и клиенте

,

изоморфный

Что если мы используем Node JS в качестве нашего сервера, чтобы мы могли запускать один и тот же код JS на обоих концах? Теперь у нас есть все наши маршруты, определенные в одной конфигурации реакции-маршрутизатора, и нам не нужно дублировать наш код рендеринга. Это, так сказать, «Святой Грааль». Сервер отправляет ту же самую разметку, которую мы получили бы в случае перехода страницы на клиенте. Это решение является оптимальным с точки зрения SEO.

Недостатки :

  • Сервер должен (быть в состоянии) запустить JS. Я экспериментировал с Java icw Nashorn, но он не работает для меня. На практике это в основном означает, что вы должны использовать сервер на базе Node JS.
  • Многие сложные экологические проблемы (использование windowна стороне сервера и т. Д.)
  • Крутая кривая обучения

,

Какой я должен использовать?

Выберите тот, который вы можете сойти с рук. Лично я думаю, что все достаточно просто настроить, так что это мой минимум. Эта настройка позволяет вам улучшать вещи с течением времени. Если вы уже используете Node JS в качестве серверной платформы, я бы определенно занялся созданием изоморфного приложения. Да, сначала это сложно, но как только вы освоитесь, это действительно очень элегантное решение проблемы.

В общем, для меня это было бы решающим фактором. Если мой сервер работает на Node JS, я бы стал изоморфным; В противном случае я бы выбрал решение Catch-all и просто расширил его (Гибридное решение), когда время идет, а требования SEO требуют этого.

Если вы хотите больше узнать об изоморфном (также называемом «универсальным») рендеринге с помощью React, есть несколько хороших учебников по этому вопросу:

Кроме того, чтобы начать, я рекомендую взглянуть на некоторые стартовые наборы. Выберите тот, который соответствует вашему выбору для технологического стека (помните, React - это просто V в MVC, вам нужно больше вещей для создания полноценного приложения). Начните с просмотра того, что опубликовано самим Facebook:

Или выберите один из множества сообществ. Теперь есть хороший сайт, который пытается проиндексировать их все:

Я начал с этого:

В настоящее время я использую самодельную версию универсального рендеринга, которая была вдохновлена ​​двумя вышеперечисленными стартовыми наборами, но сейчас они устарели.

Удачи в вашем квесте!


2
Великий пост Стейн! Вы бы порекомендовали пойти со стартовым набором для изоморфного приложения реакции? Если да, не могли бы вы привести пример, который вы бы предпочли?
Крис

1
@ Paulos3000 Это зависит от того, какой сервер вы используете. По сути, вы определяете маршрут /*и заставляете его отвечать вашей HTML-страницей. Сложность заключается в том, чтобы убедиться, что вы не перехватываете запросы на файлы .js и .css с этим маршрутом.
Стейн де Витт

2
@ Paulos3000 Смотрите здесь для некоторых связанных вопросов: для Apache / php , для Express / js , для J2E / Java .
Стейн де Витт

1
Привет, я пытаюсь решить хэш, но не повезло. Получаю createMemoryHistory is not a functionошибку. Не возражаете посмотреть? stackoverflow.com/questions/45002573/…
Леон Габан

5
@LeonGaban Кажется, с тех пор, как этот ответ был написан, React Router изменил свою реализацию. Теперь у них есть разные экземпляры Маршрутизатора для разных историй, и они выполняют настройку истории в фоновом режиме. Основные принципы все те же.
Стейн де Витт

116

Ответы здесь очень полезны, для меня работало то, что я настроил мой сервер Webpack для ожидания маршрутов.

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

HistoryApiFallback - это то, что исправило эту проблему для меня. Теперь маршрутизация работает правильно, и я могу обновить страницу или ввести URL-адрес напрямую. Не нужно беспокоиться об обходах на вашем сервере узлов. Этот ответ, очевидно, работает только при использовании веб-пакета.

РЕДАКТИРОВАТЬ: см. Мой ответ здесь для более подробной причины, почему это необходимо: https://stackoverflow.com/a/37622953/5217568


16
Обратите внимание, что команда Webpack рекомендует не использовать dev-сервер в работе.
Стейн де Витт

5
Для общих целей разработки это лучшее решение. historyApiFallbackдостаточно. Что касается всех других опций, он также может быть установлен из CLI с флагом --history-api-fallback.
Марко Лаззери,

2
@ Кунок Это не так. Это быстрое решение для разработки, но вам все равно придется что-то придумывать для производства.
Стейн де Витт

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" этот метод является Catch-all и, вероятно, самый простой способ, чтобы вы начали.


3
Это отлично работает! и самое простое, если вы не хотите реструктурировать свое приложение
mhyassin

7
Не забывайте RewriteEngine On в качестве первой строки
Kai Qing

3
Обратите внимание, что этот ответ относится к конфигурации на сервере Apache. Это не часть приложения React.
Колтон Хикс

Я не понял ответы выше. Никто из учителей не сказал, что мои ссылки вообще не будут работать вживую. Однако этот простой файл htaccess работал. Спасибо
Томас Уильямс

Это работает и для статического экспорта next.js. Поскольку tomcat не похож на узел, нам нужна эта дополнительная конфигурация для статического экспорта next.js.
Гири-Джедигунта

62

Для пользователей React Router V4 :

Если вы попытаетесь решить эту проблему с помощью техники Hash History, упомянутой в других ответах, обратите внимание, что

<Router history={hashHistory} >

не работает в V4, пожалуйста, используйте HashRouterвместо:

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

<HashRouter>
  <App/>
</HashRouter>

Ссылка: HashRouter


Не могли бы вы рассказать, почему HashRouter решает эту проблему? Ссылка, которую вы предоставили, не объясняет это для меня. Кроме того, есть ли способ скрыть хэш в пути? Я использовал BrowserRouter, но столкнулся с этой проблемой 404 ..
Ян Смит

29

Маршрутизатор можно вызывать двумя различными способами, в зависимости от того, происходит ли навигация на клиенте или на сервере. Он настроен для работы на стороне клиента. Ключевым параметром является второй после метода запуска местоположение.

Когда вы используете компонент React Router Link, он блокирует навигацию в браузере и вызывает transitionTo для выполнения клиентской навигации. Вы используете HistoryLocation, поэтому он использует API истории HTML5, чтобы завершить иллюзию навигации, имитируя новый URL в адресной строке. Если вы используете старые браузеры, это не сработает. Вам нужно будет использовать компонент HashLocation.

Когда вы нажимаете кнопку обновления, вы обходите весь код React и React Router. Сервер получает запрос /joblistи должен что-то вернуть. На сервере вам нужно передать путь, который был запрошен runметоду, чтобы он отображал правильное представление. Вы можете использовать ту же карту маршрутов, но вам, вероятно, понадобится другой вызов Router.run. Как указывает Чарльз, вы можете использовать переписывание 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 router обычно используется для обработки разных «путей» только внутри браузера. Есть два типичных способа сделать это: путь хеша старого стиля и более новый API истории. При обновлении браузера будет сделан запрос к серверу, который обойдёт ваш код реакции маршрутизатора на стороне клиента. Вам нужно будет обработать путь на сервере (но только если вы используете API истории). Для этого вы можете использовать реагирующий маршрутизатор, но вам нужно сделать что-то похожее на то, что я описал выше.
Тодд

понял. Но как быть, если сервер не на языке JS (скажем, на Java)?
VB_

2
извините ... вы имеете в виду, что вам нужен экспресс-сервер для рендеринга маршрута "не root"? Сначала я был поражен, что определение изоморфизма не включало выборку данных в его концепцию (вещи крайне сложны, если вы хотите визуализировать представление С данных на стороне сервера, хотя это было бы очевидной потребностью SEO - и это требует изоморфизм). Теперь я как "WTF реагирует", серьезно ... Поправьте меня, если я ошибаюсь, ember / angular / backbone требует серверной вещи для рендеринга маршрута? Я действительно не понимаю этого раздутого требования использовать маршруты
Бен

1
Означает ли это, что обновление в реакции-маршрутизаторе не работает, если мое приложение реакции не изоморфно?
Мэтт

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он не будет работать (если вы используете хеш-маршрутизацию)
Робби Аверилл

Тег <base href = "/"> сделал это для меня. Спасибо за это. Сервер разработки Webpack продолжал пытаться получить пакет из пути / <bundle> и терпел неудачу.
Малисбад

28

Я использовал create-реагировать на приложение, чтобы сделать веб-сайт только что и тот же вопрос, представленный здесь. Я использую BrowserRoutingиз react-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 за ссылку на официальную фб документацию. Путь
user3773048

Для справки: я использую экземпляр AWS с nginx и приложение Angular. Это работает.
Диего Сармьенто

ты спасаешь меня. я пытался решить мою проблему со вчерашнего дня, я устал пару решений, но я не смог. наконец, вы помогли мне решить проблему с маршрутизатором. ах, спасибо большое, брат
Шарифур Робин

Есть ли что-то другое, что нужно для «реакция»: «^ 16.8.6», «реакция-маршрутизатор»: «^ 5.0.1»? Я попробовал эти решения (как nginx, так и apache), и они не работают.
Марк А. Тальяферро

Поскольку 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

Затем поместите папку сборки в папку var / www /. Теперь приложение работает нормально с добавлением тега # в каждом URL. подобно

localhost / # / home localhost / # / aboutus

Решение 2. Без тега # с помощью browserHistory,

Установите вашу историю = {browserHistory} в вашем маршрутизаторе, теперь создайте ее с помощью sudo npm run build.

Вам нужно создать файл "conf" для решения страницы 404 not found, файл 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

Затем он попросит перезагрузить сервер apache, используя sudo service apache2 reload или restart.

затем откройте папку 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

Эта проблема возникла из-за того, что CloudFront ответил сообщением 403 «Отказано в доступе», так как ожидалось, что / некоторый / другой / путь будет существовать в моей папке S3, но этот путь существует только внутри маршрутизации React с реакции-маршрутизатором.

Решением было настроить правило распространения страниц ошибок. Перейдите в настройки CloudFront и выберите свой дистрибутив. Далее перейдите на вкладку «Страницы ошибок». Нажмите «Создать пользовательский ответ об ошибке» и добавьте запись для 403, так как это код ошибки, который мы получаем. Установите Путь к странице ответа на /index.html и код состояния на 200. Конечный результат поражает своей простотой. Страница индекса обслуживается, но URL-адрес сохраняется в браузере, поэтому, как только приложение реакции загружается, оно обнаруживает путь URL-адреса и переходит к нужному маршруту.

Ошибка Страницы 403 Правило


1
Это именно то, что мне было нужно. Спасибо.
Джонатан

Итак, все ваши URL работают, но возвращают код состояния 403? Это не правильно. Ожидаемые коды состояния должны быть в диапазоне 2хх.
Стейн де Витт

@StijndeWitt Я не уверен, что вы правильно поняли. CloudFront все обработает - клиент не увидит никаких кодов состояния 403. Перенаправление происходит на стороне сервера, отображает страницу индекса, поддерживает URL и в результате предоставляет правильный маршрут.
th3morg

@ th3morg Ах да, теперь я вижу. Я пропустил, что это также позволяет вам заполнить код состояния ответа как 200. Таким образом, в основном вы отображаете статус 403 в статус 200 ... Выглядит хорошо ... за исключением того, что, возможно, если вы получите 403 в результате по какой-то другой причине Вы не сможете увидеть это из-за этого.
Стейн де Витт

1
спасибо за это @ th3morg. Эта настройка также работает для многоуровневых путей? Это прекрасно работает для меня, если есть какой-либо путь на корневом уровне, например домен / регистрация, домен / логин, домен / <documentId>, но не работает для многоуровневых путей, например домен / документ / <documentId>.
Парглс


12

Если вы размещаете свое приложение реагирования в IIS, просто добавьте файл 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 для красивых URL

// 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 -p

4) запустить nodemon server.jsилиnode server.js

РЕДАКТИРОВАТЬ: Вы можете позволить nginx обрабатывать это в блоке сервера и игнорировать шаг 2:

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

получение пути неопределенного
Goutham

@Goutham В верхней части вашего файла server.js добавьте import path from 'pathилиconst path = require('path')
Исаак Пак

Шаг 2 (поймать всех с /*) только что спас мой день. Спасибо! :)
Atlas7

Это работает, для людей, использующих приставку и связанных с подключенным маршрутизатором, посмотрите этот пример: github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson

10

Если вы используете приложение Create React:

Тем не менее, эта проблема отлично подходит для решений для многих основных хостинговых платформ, которые вы можете найти ЗДЕСЬ на странице Create React App. Например, я использую React Router v4 и Netlify для своего кода внешнего интерфейса. Все, что нужно, это добавить 1 файл в мою общую папку ("_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]  

Спасибо, это то, что у меня работало на Fedora 28 с apache. Ни одно из вышеперечисленных правил переписывания, ни те, что на странице CRA, не сработали для меня. Я добавил их в настройку виртуального хоста, а не в отдельный файл .htaccess.
Boerre

6

Если у вас есть запасной вариант для вашего index.html, убедитесь, что в вашем файле index.html есть следующее:

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

Это может отличаться от проекта к проекту.


2
Добавьте это в свой HTML head: <base href = "/">
Efe Ariaroo

4

Если вы используете firebase, все, что вам нужно сделать, это убедиться, что у вас есть свойство rewrites в файле firebase.json в корне вашего приложения (в разделе хостинга).

Например:

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

Надеюсь, что это сэкономит кому-то еще кучу разочарования и потраченного времени.

Удачного кодирования ...

Дальнейшее чтение по теме:

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

Firebase CLI: «Настроить как одностраничное приложение (переписать все URL в /index.html)»


Вы легенда
Perniferous

4

Я нашел решение для моего SPA с реагирующим маршрутизатором (Apache). Просто добавьте в .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>

источник: https://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042


3

Я пока не использую рендеринг на стороне сервера, но столкнулся с той же проблемой, что и операционная система, в которой Link в большинстве случаев работал нормально, но не получался, когда у меня был параметр. Я запишу здесь свое решение, чтобы посмотреть, поможет ли оно кому-нибудь.

Мой основной JSX содержит это:

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

Это прекрасно работает для первой подходящей ссылки, но когда изменяется: id <Link> выражениях, вложенных в страницу сведений об этой модели, URL-адрес изменяется в панели браузера, но содержимое страницы изначально не изменялось, чтобы отражать связанную модель.

Беда в том, что я использовал props.params.idдля установки модели componentDidMount. Компонент монтируется только один раз, так что это означает, что первая модель - это та, которая прикрепляется на странице, а последующие ссылки изменяют реквизиты, но оставляют страницу неизменной.

Установка модели в состояние компонента в обоих componentDidMountи в componentWillReceiveProps(где это основано на следующих реквизитах) решает проблему, и содержимое страницы изменяется, чтобы отразить желаемую модель.


1
Может быть, лучше использовать конструктор компонента (который также имеет доступ props) iso, componentDidMountесли вы когда-нибудь захотите попробовать рендеринг на стороне сервера. Потому что componentDidMountвызывается только в браузере. Его цель - делать с DOM такие вещи, как присоединение слушателей событий и bodyт. Д., С которыми вы не можете работать render.
Стейн де Витт

3

Эта тема немного устарела и решаема, но я хотел бы предложить вам простое, ясное и лучшее решение. Это работает, если вы используете веб-сервер.

Каждый веб-сервер имеет возможность перенаправить пользователя на страницу ошибки в случае 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>

Пример:

  • ПОЛУЧИТЬ http://example.com/about
  • Веб-сервер выдает http 404, потому что эта страница не существует на стороне сервера
  • конфигурация страницы ошибки сообщает серверу, что отправляет страницу index.jsp обратно пользователю
  • тогда JS выполнит остальную часть работы на стороне клиента, потому что URL на стороне клиента все еще http://example.com/about .

Вот и все, больше никаких магических потребностей :)


Это было потрясающе! Я использую сервер Wildfly 10.1 и сделал это обновление для моего файла web.xml, за исключением того, что для местоположения было установлено просто '/'. Это было потому, что в моем коде реагирования я использовал историю браузера следующим образом: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

@ dayanrr91, если у вашего проекта есть web api, вам все равно не понадобится маршрутизация на стороне сервера. И ваш реактивный маршрутизатор, я считаю, должен прочитать URL-адрес и сделать соответствующую маршрутизацию. Ничто не должно блокировать это
Elnoor

Ваш комментарий очень важен, но тогда у меня есть вопрос, я просто добавил HashRouter, но теперь, когда я вхожу на любой URL-адрес вручную, он будет перенаправлять на мой домашний компонент вместо перенаправления на мой дом, а затем на компонент, который я пытался входя, есть ли обходной путь для этого?
dayanrr91

@ dayanrr91 Не знаю, на самом деле, никогда не пробовал hashrouter, но я думаю, что это должно быть связано с вашим кодом. Работа hashrouter должна быть похожа на browserrouter. Кроме того, я только что проверил здесь, в этой песочнице, он отлично работает codesandbox.io/s/25okp1mny , протестируйте URL 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

В случае, если кто-нибудь здесь ищет решение для React JS SPA с Laravel. Принятый ответ - лучшее объяснение того, почему такие проблемы случаются. Как уже объяснялось, вы должны настроить как клиентскую, так и серверную сторону. В свой шаблон лезвия включите файл 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', он сначала загружается на стороне сервера. Поскольку на стороне сервера в этом месте ничего нет, возвращается не найдено. Когда у вас есть этот дополнительный маршрутизатор, он загружает ту же страницу, и загружается реагирующий маршрутизатор, а затем реагирующий загрузчик решает, какой компонент показать. Надеюсь это поможет.


Можно ли загрузить один точный URI на сервер, а затем сервер перенаправить на React? Например, когда я загружаю /user/{userID}, мне просто нужно вернуть универсальное представление /userHTML, принести идентификатор и вызвать его, используя 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, у меня была такая же проблема Решение => В вашем файле 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

Использование HashRouterработало для меня с Redux также, просто замените:

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 / * на стороне сервера, чтобы избавиться от этой проблемы.

Я пошел с этим подходом, потому что даже собственный API истории HTML5 не поддерживает правильное перенаправление при обновлении страницы (насколько я знаю).

Примечание. В выбранном ответе это уже учтено, но я пытаюсь быть более конкретным.

Экспресс Маршрут

Тест - История API Протестировал и просто хотел этим поделиться.

Надеюсь, поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.