Les URL React-router ne fonctionnent pas lors de l'actualisation ou de l'écriture manuelle


655

J'utilise React-router et cela fonctionne bien lorsque je clique sur les boutons de lien, mais lorsque je rafraîchis ma page Web, il ne charge pas ce que je veux.

Par exemple, je suis localhost/joblistdedans et tout va bien parce que je suis arrivé ici en appuyant sur un lien. Mais si je rafraîchis la page Web, j'obtiens:

Cannot GET /joblist

Par défaut, cela ne fonctionnait pas comme ça. Au départ, j'avais mon URL au fur localhost/#/et à mesure localhost/#/joblistet elles fonctionnaient parfaitement bien. Mais je n'aime pas ce type d'URL, alors en essayant de l'effacer #, j'ai écrit:

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

Ce problème ne se produit pas avec localhost/, celui-ci renvoie toujours ce que je veux.

EDIT: Cette application est d'une seule page, donc /joblistn'a pas besoin de demander quoi que ce soit à n'importe quel serveur.

EDIT2: Mon routeur entier.

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);
});

à moins que vous n'utilisiez htaccess pour charger votre page de visite principale et que votre routeur utilise location.pathname, cela ne fonctionnera pas.
Charles John Thompson III

Comment avez-vous effacé ce #symbole? Je vous remercie!
SudoPlz

4
Si vous hébergez votre application React dans un compartiment S3, vous pouvez simplement définir le document d'erreur sur index.html. Cela vous assurera d'être index.htmltouché quoi qu'il arrive.
Trevor Hutto

Dans mon cas, cela fonctionne bien sous Windows mais pas sous Linux
Ejaz Karim

C'est la référence qui a aidé à résoudre mon problème: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

Réponses:


1096

En regardant les commentaires sur la réponse acceptée et la nature générique de cette question («ne fonctionne pas»), j'ai pensé que cela pourrait être un bon endroit pour quelques explications générales sur les problèmes impliqués ici. Cette réponse est donc conçue comme une information / élaboration de base sur le cas d'utilisation spécifique de l'OP. S'il vous plaît, supportez-moi.

Côté serveur vs côté client

La première chose importante à comprendre à ce sujet est qu'il y a maintenant 2 endroits où l'URL est interprétée, alors qu'il n'y en avait qu'un dans «l'ancien temps». Dans le passé, lorsque la vie était simple, certains utilisateurs envoyaient une demande http://example.com/aboutau serveur, qui inspectait la partie chemin de l'URL, déterminait que l'utilisateur demandait la page à propos, puis renvoyait cette page.

Avec le routage côté client, ce que fournit React-Router, les choses sont moins simples. Au début, le client n'a pas encore de code JS chargé. Ainsi, la toute première demande sera toujours adressée au serveur. Cela retournera alors une page qui contient les balises de script nécessaires pour charger React et React Router, etc. Ce n'est que lorsque ces scripts ont été chargés que la phase 2 démarre. Dans la phase 2, lorsque l'utilisateur clique sur le lien de navigation `` A propos de nous '' par exemple, l'URL est modifiée localement uniquement en http://example.com/about(rendue possible par l' API Historique ), mais aucune demande au serveur n'est faite. Au lieu de cela, React Router fait son travail côté client, détermine la vue React à restituer et la restitue. En supposant que votre page À propos n'a pas besoin d'appeler REST, c'est déjà fait. Vous êtes passé de Home à About Us sans qu'aucune demande de serveur n'ait été déclenchée.

Donc, fondamentalement, lorsque vous cliquez sur un lien, certaines exécutions Javascript manipulent l'URL dans la barre d'adresse, sans provoquer d'actualisation de la page , ce qui entraîne à son tour React Router à effectuer une transition de page côté client .

Mais considérez maintenant ce qui se passe si vous copiez-collez l'URL dans la barre d'adresse et l'envoyez par e-mail à un ami. Votre ami n'a pas encore chargé votre site Web. En d'autres termes, elle est toujours en phase 1 . Aucun React Router n'est encore en cours d'exécution sur sa machine. Son navigateur va donc faire une demande au serveurhttp://example.com/about .

Et c'est là que commence votre problème. Jusqu'à présent, vous pouviez vous en sortir en plaçant simplement un code HTML statique à la racine Web de votre serveur. Mais cela donnerait des 404erreurs pour toutes les autres URL à la demande du serveur . Ces mêmes URL fonctionnent très bien côté client , car React Router effectue le routage pour vous, mais elles échouent côté serveur, sauf si vous les faites comprendre à votre serveur.

Combinaison du routage côté serveur et côté client

Si vous souhaitez que l' http://example.com/aboutURL fonctionne à la fois côté serveur et côté client, vous devez configurer des itinéraires pour celle-ci à la fois côté serveur et côté client. Est-ce logique, non?

Et c'est là que vos choix commencent. Les solutions vont du contournement complet du problème, via une route fourre-tout qui renvoie le HTML d'amorçage, à l'approche isomorphe complète où le serveur et le client exécutent le même code JS.

.

Contourner complètement le problème: Hash History

Avec l' historique de hachage au lieu de l' historique du navigateur , votre URL pour la page à propos ressemblerait à ceci: http://example.com/#/about La partie après le #symbole hash ( ) n'est pas envoyée au serveur. Ainsi, le serveur ne voit http://example.com/et n'envoie que la page d'index comme prévu. React-Router ramassera la #/aboutpièce et affichera la bonne page.

Inconvénients :

  • URL «moches»
  • Le rendu côté serveur n'est pas possible avec cette approche. En ce qui concerne l'optimisation des moteurs de recherche (SEO), votre site Web se compose d'une seule page avec pratiquement aucun contenu.

.

Fourre-tout

Avec cette approche vous utilisez l' historique du navigateur, mais juste mettre en place un fourre-tout sur le serveur qui envoie /*à index.html, vous donnant effectivement la même situation avec Hash Histoire. Cependant, vous avez des URL propres et vous pouvez améliorer ce schéma plus tard sans avoir à invalider tous les favoris de vos utilisateurs.

Inconvénients :

  • Plus complexe à mettre en place
  • Toujours pas de bon référencement

.

Hybride

Dans l'approche hybride, vous développez le scénario fourre-tout en ajoutant des scripts spécifiques pour des itinéraires spécifiques. Vous pouvez créer des scripts PHP simples pour renvoyer les pages les plus importantes de votre site avec du contenu inclus, afin que Googlebot puisse au moins voir ce qu'il y a sur votre page.

Inconvénients :

  • Encore plus complexe à mettre en place
  • Seul bon référencement pour les itinéraires auxquels vous accordez le traitement spécial
  • Duplication de code pour le rendu de contenu sur le serveur et le client

.

Isomorphe

Et si nous utilisons Node JS comme serveur pour pouvoir exécuter le même code JS aux deux extrémités? Maintenant, nous avons tous nos itinéraires définis dans une seule configuration de routeur de réaction et nous n'avons pas besoin de dupliquer notre code de rendu. C'est pour ainsi dire «le Saint Graal». Le serveur envoie exactement le même balisage que celui auquel nous nous retrouverions si la transition de page s'était produite sur le client. Cette solution est optimale en termes de référencement.

Inconvénients :

  • Le serveur doit (pouvoir) exécuter JS. J'ai expérimenté avec Java icw Nashorn mais cela ne fonctionne pas pour moi. En pratique, cela signifie principalement que vous devez utiliser un serveur basé sur Node JS.
  • Beaucoup de problèmes environnementaux délicats (utilisation windowcôté serveur, etc.)
  • Courbe d'apprentissage abrupte

.

Que dois-je utiliser?

Choisissez celui avec lequel vous pouvez vous en sortir. Personnellement, je pense que le fourre-tout est assez simple à mettre en place, ce serait donc mon minimum. Cette configuration vous permet d'améliorer les choses au fil du temps. Si vous utilisez déjà Node JS comme plate-forme de serveur, j'envisagerais certainement de faire une application isomorphe. Oui, c'est difficile au début, mais une fois que vous avez compris, c'est en fait une solution très élégante au problème.

Donc, fondamentalement, pour moi, ce serait le facteur décisif. Si mon serveur fonctionne sur Node JS, je deviendrais isomorphe; Sinon, j'opterais pour la solution Catch-all et je développerais simplement celle-ci (solution hybride) au fur et à mesure que le temps et les exigences SEO l'exigent.

Si vous souhaitez en savoir plus sur le rendu isomorphe (également appelé «universel») avec React, il existe de bons tutoriels sur le sujet:

Aussi, pour commencer, je vous recommande de regarder quelques kits de démarrage. Choisissez-en un qui correspond à vos choix pour la pile technologique (rappelez-vous, React n'est que le V dans MVC, vous avez besoin de plus de choses pour créer une application complète). Commencez par regarder celui publié par Facebook lui-même:

Ou choisissez l'un des nombreux par la communauté. Il y a maintenant un joli site qui essaie de les indexer tous:

J'ai commencé avec ceux-ci:

Actuellement, j'utilise une version maison de brassage universel qui a été inspirée par les deux kits de démarrage ci-dessus, mais ils sont désormais obsolètes.

Bonne chance dans ta quête!


2
Great post Stijn! Recommanderiez-vous d'utiliser un kit de démarrage pour une application de réaction isomorphe? Si oui, pourriez-vous donner un exemple de celui que vous préférez?
Chris

1
@ Paulos3000 Cela dépend du serveur que vous utilisez. Fondamentalement, vous définissez un itinéraire /*et faites-le répondre avec votre page HTML. La chose délicate ici est de vous assurer que vous n'interceptez pas les demandes pour les fichiers .js et .css avec cette route.
Stijn de Witt

2
@ Paulos3000 Voir ici pour quelques questions connexes: pour Apache / php , pour Express / js , pour J2E / Java .
Stijn de Witt

1
Salut, j'essaie la solution de hachage, mais pas de chance. Obtenir une createMemoryHistory is not a functionerreur. Ça te regarde? stackoverflow.com/questions/45002573/…
Leon Gaban

5
@LeonGaban Il semble que depuis que cette réponse a été écrite, React Router a changé leur implémentation. Ils ont maintenant différentes instances de routeur pour les différents historiques et ils font la configuration de l'historique en arrière-plan. Les principes de base sont toujours les mêmes.
Stijn de Witt

116

Les réponses ici sont toutes extrêmement utiles, ce qui a fonctionné pour moi a été de configurer mon serveur Webpack pour attendre les itinéraires.

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

L'historiqueApiFallback est ce qui a résolu ce problème pour moi. Maintenant, le routage fonctionne correctement et je peux rafraîchir la page ou saisir directement l'URL. Pas besoin de vous soucier des solutions de contournement sur votre serveur de noeud. Cette réponse ne fonctionne évidemment que si vous utilisez webpack.

EDIT: voir ma réponse ici pour une raison plus détaillée pour laquelle cela est nécessaire: https://stackoverflow.com/a/37622953/5217568


16
Notez que l'équipe Webpack recommande de ne pas utiliser le serveur de développement en production.
Stijn de Witt

5
À des fins de développement générique, c'est la meilleure solution. historyApiFallbackest suffisant. Comme pour toutes les autres options, il peut également être défini à partir de CLI avec le drapeau --history-api-fallback.
Marco Lazzeri

2
@Kunok Ce n'est pas le cas. C'est une solution rapide pour le développement, mais vous devrez toujours trouver quelque chose pour la production.
Stijn de Witt

contentBase: ': /' car vos fichiers d'application sont accessibles depuis l'url
Nezih

85

Vous pouvez modifier votre .htaccessfichier et insérer ceci:

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

j'utilise react: "^16.12.0" et react-router: "^5.1.2" cette méthode est le fourre-tout et est probablement le moyen le plus simple de vous aider à démarrer.


3
Cela fonctionne bien! et le plus simple si vous ne voulez pas restructurer votre application
mhyassin

7
N'oubliez pas RewriteEngine On comme première ligne
Kai Qing

3
Notez que cette réponse fait référence à la configuration sur un serveur Apache. Cela ne fait pas partie d'une application React.
Colton Hicks

Je n'ai pas compris les réponses ci-dessus. Aucun des tutoriels n'a dit que mes liens ne fonctionneraient pas du tout en direct. Cependant, ce simple fichier htaccess a fonctionné. Merci
Thomas Williams

Cela fonctionne également pour l'exportation statique next.js. Comme le tomcat n'est pas similaire au nœud, nous avons besoin de cette configuration supplémentaire pour l'exportation statique next.js.
giri-jeedigunta

62

Pour React Router V4 :

Si vous essayez de résoudre ce problème par la technique de Hash History mentionnée dans d'autres réponses, notez que

<Router history={hashHistory} >

ne fonctionne pas en V4, veuillez utiliser à la HashRouterplace:

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

<HashRouter>
  <App/>
</HashRouter>

Référence: HashRouter


Pourriez-vous expliquer en détail pourquoi le HashRouter résout ce problème? Le lien que vous avez fourni ne l'explique pas pour moi. De plus, existe-t-il un moyen de cacher le hachage sur le chemin? J'utilisais le BrowserRouter mais j'ai rencontré ce problème 404 avec lui.
Ian Smith

29

Le routeur peut être appelé de deux manières différentes, selon que la navigation s'effectue sur le client ou sur le serveur. Vous l'avez configuré pour un fonctionnement côté client. Le paramètre clé est le deuxième de la méthode d'exécution , l'emplacement.

Lorsque vous utilisez le composant React Router Link, il bloque la navigation du navigateur et appelle transitionTo pour effectuer une navigation côté client. Vous utilisez HistoryLocation, il utilise donc l'API d'historique HTML5 pour compléter l'illusion de navigation en simulant la nouvelle URL dans la barre d'adresse. Si vous utilisez des navigateurs plus anciens, cela ne fonctionnera pas. Vous devez utiliser le composant HashLocation.

Lorsque vous appuyez sur Actualiser, vous contournez tout le code React et React Router. Le serveur reçoit la demande /joblistet doit renvoyer quelque chose. Sur le serveur, vous devez transmettre le chemin d'accès qui a été demandé à la runméthode pour qu'elle affiche la vue correcte. Vous pouvez utiliser la même carte d'itinéraire, mais vous aurez probablement besoin d'un appel différent vers Router.run. Comme le souligne Charles, vous pouvez utiliser la réécriture d'URL pour gérer cela. Une autre option consiste à utiliser un serveur node.js pour gérer toutes les demandes et transmettre la valeur du chemin d'accès comme argument d'emplacement.

En express, par exemple, cela pourrait ressembler à ceci:

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 });
    });
});

Notez que le chemin de demande est transmis à run. Pour ce faire, vous devez disposer d'un moteur de vue côté serveur auquel vous pouvez transmettre le code HTML rendu. Il existe un certain nombre d'autres considérations lors de l'utilisation renderToStringet de l'exécution de React sur le serveur. Une fois que la page est rendue sur le serveur, lorsque votre application se charge dans le client, elle s'affichera à nouveau, mettant à jour le HTML rendu côté serveur selon les besoins.


2
excuse Excusez-moi, pouvez-vous expliquer la déclaration suivante "lorsque vous appuyez sur Actualiser, vous contournez tout le code React et React Router"? Pourquoi cela arrive-t-il?
VB_

Le routeur React est normalement utilisé pour gérer différents «chemins» dans le navigateur uniquement. Il existe deux façons typiques de le faire: l'ancien chemin de hachage de style et l'API d'historique plus récente. Une actualisation du navigateur fera une demande de serveur, qui contournera votre code de routeur de réaction côté client. Vous devrez gérer le chemin sur le serveur (mais uniquement si vous utilisez l'API d'historique). Vous pouvez utiliser le routeur React pour cela, mais vous devrez faire quelque chose de similaire à ce que j'ai décrit ci-dessus.
Todd

je l'ai. Mais comment faire si le serveur est en langage non JS (disons Java)?
VB_

2
désolé… vous voulez dire que vous avez besoin d'un serveur express pour rendre un itinéraire qui n'est pas "root"? J'ai d'abord été étonné que la définition de l'isomorphisme n'inclue pas la récupération de données dans son concept (les choses sont désespérément complexes si vous souhaitez afficher la vue AVEC des données côté serveur, bien que ce soit un besoin évident de référencement - et l'isomorphisme le prétend). Maintenant, je suis comme "WTF react", sérieusement ... Corrigez-moi si je me trompe, est-ce que les braises / angulaires / backbone nécessitent un serveur pour rendre un itinéraire? Je ne comprends vraiment pas cette obligation gonflée d'utiliser des routes
Ben

1
Cela signifie-t-il que l'actualisation dans react-router ne fonctionne pas si mon application react n'est pas isomorphe?
Matt

28

Dans votre index.html head, ajoutez ce qui suit:

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

Ensuite, lors de l'exécution avec le serveur de développement webpack, utilisez cette commande.

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

--history-api-fallback est la partie importante


Cela a très bien fonctionné! Des liens pour en savoir plus sur comment vous avez obtenu / trouvé cette solution?
justDan

1
Désolé frère. Je l'ai trouvé grâce à la technique éprouvée d'essais et d'erreurs.
Efe Ariaroo

Il convient de mentionner que si vous utilisez un href de base qui ne sont pas /alors HashRouterne fonctionnera pas (si vous utilisez le routage hachage)
Robbie Averill

La balise <base href = "/"> l'a fait pour moi. Merci pour cela. Le serveur de développement Webpack a continué à essayer de récupérer le bundle du chemin / <bundle> et échouait.
Malisbad

28

J'ai utilisé create-react-app pour créer un site Web tout à l'heure et j'ai présenté le même problème ici. J'utilise BrowserRoutingdu react-router-dompaquet. Je suis en cours d'exécution sur un serveur Nginx et ce qui a résolu pour moi l'ajout de ce qui suit à/etc/nginx/yourconfig.conf

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

Ce qui correspond à l'ajout de ce qui suit au .htaccesscas où vous exécutez Appache

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

Cela semble également être la solution suggérée par Facebook eux-mêmes et peut être trouvée ici


1
Wow, une solution si simple pour nginx; fonctionne comme un charme, en tant que nouvel utilisateur de nginx. Copiez-collez et c'est prêt. +1 pour le lien vers la documentation fb officielle. Marche à suivre.
user3773048

Pour mémoire: j'utilise une instance AWS avec nginx et une application angulaire. Cela marche.
Diego Sarmiento du

tu m'as sauvé. J'essayais de résoudre mon problème d'hier, j'ai fatigué quelques solutions mais j'ai échoué. enfin vous m'avez aidé à résoudre ce problème de routeur. ah merci beaucoup frère
Sharifur Robin

Existe-t-il quelque chose de différent pour "react": "^ 16.8.6", "react-router": "^ 5.0.1"? J'ai essayé ces solutions (à la fois nginx et apache) et elles ne fonctionnent pas.
Mark A. Tagliaferro

Comme Facebook n'a pas modifié sa documentation à ce sujet, je ne peux que supposer que la procédure est la même. Je ne l'ai pas essayé depuis longtemps cependant.
Aidin

17

Cela peut résoudre votre problème

J'ai également rencontré le même problème dans l'application ReactJS en mode Production. Voici la 2 solution au problème.

1.Changez l'historique de routage en "hashHistory" au lieu de browserHistory à la place de

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

Maintenant, créez l'application en utilisant la commande

sudo npm run build

Ensuite, placez le dossier de construction dans votre dossier var / www /, Maintenant, l'application fonctionne correctement avec l'ajout de la balise # dans chaque URL. comme

localhost / # / home localhost / # / aboutus

Solution 2: sans balise # à l'aide de browserHistory,

Définissez votre historique = {browserHistory} dans votre routeur, maintenant construisez-le en utilisant sudo npm run build.

Vous devez créer le fichier "conf" pour résoudre la page 404 introuvable, le fichier conf devrait être comme ceci.

ouvrez votre terminal tapez les commandes ci-dessous

cd / etc / apache2 / sites-available ls nano sample.conf Ajoutez-y le contenu ci-dessous.

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

Vous devez maintenant activer le fichier sample.conf à l'aide de la commande suivante

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

puis il vous demandera de recharger le serveur apache, en utilisant sudo service apache2 reload or restart

puis ouvrez votre dossier localhost / build et ajoutez le fichier .htaccess avec le contenu ci-dessous.

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

Maintenant, l'application fonctionne normalement.

Remarque: remplacez 0.0.0.0 ip par votre adresse ip locale.

En cas de doute à ce sujet, n'hésitez pas à formuler un commentaire.

J'espère que cela sera utile aux autres.


La première solution fonctionnera- "react-router-dom": "^5.1.2"t-elle?, J'utilise<BrowserRouter>
151291

16

Si vous hébergez une application React via AWS Static S3 Hosting & CloudFront

Ce problème s'est présenté par CloudFront en répondant avec un message 403 Access Denied car il s'attendait à ce que / some / other / path existe dans mon dossier S3, mais ce chemin n'existe qu'en interne dans le routage de React avec react-router.

La solution était de mettre en place une règle de distribution des pages d'erreur. Accédez aux paramètres CloudFront et choisissez votre distribution. Allez ensuite dans l'onglet "Pages d'erreur". Cliquez sur "Créer une réponse d'erreur personnalisée" et ajoutez une entrée pour 403 car c'est le code d'état d'erreur que nous obtenons. Définissez le chemin de la page de réponse sur /index.html et le code d'état sur 200. Le résultat final m'étonne par sa simplicité. La page d'index est servie, mais l'URL est conservée dans le navigateur, donc une fois que l'application React se charge, elle détecte le chemin de l'URL et navigue vers l'itinéraire souhaité.

Pages d'erreur 403 règle


1
C'est exactement ce dont j'avais besoin. Je vous remercie.
Jonathan

Alors .. toutes vos URL fonctionnent, mais renvoyez le code de statut 403? Ce n'est pas correct. Les codes d'état attendus doivent être dans la plage 2xx.
Stijn de Witt

@StijndeWitt Je ne suis pas sûr que vous compreniez bien. CloudFront gérera tout - le client ne verra aucun code d'état 403. Le réacheminement se produit côté serveur, affiche la page d'index, conserve l'URL et fournit la route correcte en conséquence.
th3morg

@ th3morg Ah oui je vois maintenant. J'ai manqué que cela vous permette également de remplir le code d'état de réponse comme 200. Donc, fondamentalement, vous mappez le statut 403 au statut 200 ... Ça va bien ... sauf que peut-être si vous obtenez le 403 à la suite pour une autre raison vous ne pourrez pas le voir à cause de cela.
Stijn de Witt

1
merci pour ce @ th3morg. Cette configuration fonctionne-t-elle également pour les chemins à plusieurs niveaux? Cela fonctionne très bien pour moi si un chemin au niveau racine, comme domaine / inscription, domaine / connexion, domaine / <documentId>, mais il ne fonctionne pas pour les chemins à plusieurs niveaux, comme domaine / document / <documentId>.
Pargles

16

Le serveur de développement Webpack a une option pour l'activer. Ouvrez package.jsonet ajoutez --history-api-fallback. Ces solutions ont fonctionné pour moi.

réagir-routeur-tutoriel


3
C'est bien, webpack-dev-servermais comment puis-je résoudre ce problème pour la génération de production?
Hussain

12

Si vous hébergez votre application React sur IIS, ajoutez simplement un fichier web.config contenant:

<?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>

Cela indiquera au serveur IIS de renvoyer la page principale au client au lieu d'une erreur 404 et pas besoin d'utiliser l'historique de hachage.


Merci mec!!!! c'est du travail!
Thanh Bao

12

Ajoutez ceci à webpack.config.js:

devServer: {
    historyApiFallback: true
}

C'est génial pour les développeurs, mais cela n'aide pas avec les builds de production.
KFunk

11

Pile de production: React, React Router v4, BrowswerRouter, Express, Nginx

1) User BrowserRouter pour de jolies URL

// app.js

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

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

2) Ajoutez index.html à toutes les demandes inconnues en utilisant /*

// 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) bundle webpack avec webpack -p

4) exécuter nodemon server.jsounode server.js

EDIT: vous souhaiterez peut-être laisser nginx gérer cela dans le bloc serveur et ignorer l'étape 2:

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

obtenir le chemin de l'indéfini
Goutham

@Goutham En haut de votre fichier server.js ajoutez soit import path from 'pathouconst path = require('path')
Isaac Pak

l'étape 2 (attraper tout avec /*) vient de me sauver la journée. Je vous remercie! :)
Atlas7

Cela fonctionne, pour les personnes utilisant redux et concernées par un routeur connecté, voir cet exemple: github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson

10

Si vous utilisez l'application Create React:

Il y a cependant une bonne marche à suivre pour résoudre ce problème avec des solutions pour de nombreuses plates-formes d'hébergement majeures que vous pouvez trouver ICI sur la page de l'application Create React. Par exemple, j'utilise React Router v4 et Netlify pour mon code frontal. Tout ce qu'il a fallu a été d'ajouter 1 fichier à mon dossier public ("_redirects") et une ligne de code dans ce fichier:

/*  /index.html  200

Maintenant, mon site Web affiche correctement des chemins tels que mysite.com/pricing lorsqu'il est entré dans le navigateur ou lorsque quelqu'un frappe actualiser.


7

Essayez d'ajouter le fichier ".htaccess" dans le dossier public avec le code ci-dessous.

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

RewriteRule ^ /index.html [L]  

Merci, c'est ce qui a fonctionné pour moi sur Fedora 28 avec apache. Aucune des règles de réécriture ci-dessus ni celles de la page de l'ARC n'ont fonctionné pour moi. Je les ai ajoutés à la configuration de l'hôte virtuel au lieu de dans un fichier .htaccess distinct.
boerre

6

Si vous avez un repli sur votre index.html, assurez-vous que dans votre fichier index.html vous avez ceci:

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

Cela peut différer d'un projet à l'autre.


2
Ajoutez ceci à votre html head: <base href = "/">
Efe Ariaroo

4

Si vous utilisez Firebase, tout ce que vous avez à faire est de vous assurer que vous avez une propriété de réécriture dans votre fichier firebase.json à la racine de votre application (dans la section hébergement).

Par exemple:

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

J'espère que cela fera économiser à quelqu'un d'autre un trésor de frustration et de perte de temps.

Bon codage ...

Pour en savoir plus sur le sujet:

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

Firebase CLI: "Configurer comme une application d'une seule page (réécrire toutes les URL dans /index.html)"


Vous êtes une légende
Perniferous


3

Je n'utilise pas encore le rendu côté serveur mais j'ai rencontré le même problème que l'OP où Link semblait bien fonctionner la plupart du temps mais a échoué lorsque j'avais un paramètre. Je vais documenter ma solution ici pour voir si elle aide quelqu'un.

Mon jsx principal contient ceci:

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

Cela fonctionne bien pour le premier lien correspondant, mais lorsque: id change dans <Link> expressions imbriquées sur la page de détails de ce modèle, l'url change dans la barre de navigateur, mais le contenu de la page n'a pas initialement changé pour refléter le modèle lié.

Le problème était que j'avais utilisé le props.params.idpour définir le modèle componentDidMount. Le composant n'est monté qu'une seule fois, ce qui signifie que le premier modèle est celui qui reste sur la page et les liens suivants modifient les accessoires mais laissent la page inchangée.

La définition du modèle dans l'état du composant à la fois componentDidMountet dans componentWillReceiveProps(où il est basé sur les accessoires suivants) résout le problème et le contenu de la page change pour refléter le modèle souhaité.


1
Il peut être préférable d'utiliser le constructeur de composants (qui a également accès à props) iso componentDidMountsi vous souhaitez essayer le rendu côté serveur. Parce componentDidMountque n'est appelé que dans le navigateur. Son but est de faire des choses avec le DOM, comme attacher des écouteurs d'événements à bodyetc. que vous ne pouvez pas faire render.
Stijn de Witt

3

Ce sujet est un peu ancien et résolu mais je voudrais vous proposer une solution simple, claire et meilleure. Cela fonctionne si vous utilisez un serveur Web.

Chaque serveur Web a la possibilité de rediriger l'utilisateur vers une page d'erreur dans le cas de http 404. Pour résoudre ce problème, vous devez rediriger l'utilisateur vers la page d'index.

Si vous utilisez un serveur de base Java (tomcat ou tout autre serveur d'applications Java), la solution pourrait être la suivante:

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>

Exemple:

  • OBTENEZ http://example.com/about
  • Le serveur Web envoie http 404 car cette page n'existe pas côté serveur
  • la configuration de la page d'erreur indique au serveur qui renvoie la page index.jsp à l'utilisateur
  • alors JS fera le reste du travail côté client car l'url du côté client est toujours http://example.com/about .

Ça y est, plus besoin de magie :)


C'était génial! J'utilise le serveur Wildfly 10.1 et j'ai fait cette mise à jour dans mon fichier web.xml, sauf que l'emplacement était réglé sur '/'. En effet, dans mon code de réaction, j'ai utilisé l'historique du navigateur comme ceci:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
Êtes-vous sûr que cela n'affecte pas le référencement? Vous donnez un statut 404 aux pages qui existent réellement. L'utilisateur pourrait ne jamais s'en rendre compte, mais les bots accordent beaucoup d'attention, en fait tellement, qu'ils ne gratteront pas votre page.
subharb

3

Si vous utilisez Express ou un autre framework dans le backend, vous pouvez ajouter la configuration similaire comme ci-dessous et vérifier le chemin public Webpack dans la configuration, cela devrait fonctionner correctement même lors du rechargement si vous utilisez BrowserRouter

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

c'est la solution la plus simple. notez que cet itinéraire devrait aller après tout autre itinéraire car c'est un fourre-tout
dcsan

3

Correction de l'erreur "ne peut pas obtenir / URL" lors de l'actualisation ou lors de l'appel direct de l'URL.

Configurez votre webpack.config.js pour attendre le lien donné les itinéraires comme celui-ci.

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

2

Comme j'utilise .Net Core MVC, quelque chose comme ça m'a aidé:

    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");
        }
   }

Fondamentalement, du côté MVC, toutes les routes qui ne correspondent pas tomberont Home/Indexcomme indiqué dans startup.cs. À l'intérieur, Indexil est possible d'obtenir l'URL de demande d'origine et de la transmettre où vous en avez besoin.

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" });
        });

Si l'API Web est séparée de l'application, comment allez-vous gérer cela?
dayanrr91

@ dayanrr91 si votre projet a une API web, vous n'aurez de toute façon pas besoin de routage côté serveur. Et votre react-router devrait, je crois, lire l'url et faire le routage approprié. Rien ne devrait le bloquer
Elnoor

votre commentaire est très apprécié, mais j'ai une question, je viens d'ajouter HashRouter, mais maintenant, lorsque j'entre manuellement dans une URL, il sera redirigé vers mon composant home au lieu de rediriger vers mon home puis vers le composant que j'essayais de entrer, y a-t-il une solution pour cela?
dayanrr91

@ dayanrr91 aucune idée vraiment, je n'ai jamais essayé hashrouter mais je pense que cela devrait être quelque chose à voir avec votre code. Le fonctionnement du hashrouter doit être similaire à celui du navigateur. Aussi je viens de tester ici dans ce bac à sable, cela fonctionne très bien codesandbox.io/s/25okp1mny , testez l'url 25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

Si vous hébergez dans IIS; L'ajout de ceci à ma configuration Web a résolu mon problème

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

Vous pouvez effectuer une configuration similaire pour tout autre serveur


1

Au cas où quelqu'un chercherait une solution sur React JS SPA avec Laravel. La réponse acceptée est la meilleure explication de la raison pour laquelle de tels problèmes se produisent. Comme déjà expliqué, vous devez configurer à la fois côté client et côté serveur. Dans votre modèle de lame, incluez le fichier js fourni, assurez-vous de l'utiliser URL facadecomme ceci

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

Dans vos itinéraires, assurez-vous de l'ajouter au point de terminaison principal où se trouve le modèle de lame. Par exemple,

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

Ce qui précède est le principal critère d'évaluation du modèle de lame. Maintenant, ajoutez également un itinéraire facultatif,

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

Le problème qui se produit est que le modèle de lame est d'abord chargé, puis le routeur React. Ainsi, lorsque vous chargez '/setting-alerts', il charge le html et le js. Mais lorsque vous chargez '/setting-alerts/about', il se charge d'abord côté serveur. Du côté serveur, il n'y a rien sur cet emplacement, il retourne non trouvé. Lorsque vous avez ce routeur en option, il charge la même page et le routeur React est également chargé, puis React Loader décide du composant à afficher. J'espère que cela t'aides.


Est-il possible de charger un URI exact sur le serveur puis le serveur fait une redirection vers React? Comme par exemple, lorsque je charge /user/{userID}, j'ai juste besoin de retourner une /uservue HTML universelle , d'apporter l'ID et de l'appeler en utilisant AJAX ou axios. Est-il possible de faire ça? sont-ils compatibles avec le référencement?
Ryuujo

1

Pour ceux qui utilisent IIS 10, voici ce que vous devez faire pour corriger ce problème. Assurez-vous que vous utilisez browserHistory avec cela. En ce qui concerne la référence, je donnerai le code pour le routage, mais ce n'est pas ce qui compte, ce qui compte c'est la prochaine étape après le code composant ci-dessous:

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"));

Étant donné que le problème est qu'IIS reçoit une demande des navigateurs clients, il interprète l'URL comme s'il demandait une page, puis renvoie une page 404 car il n'y a pas de page disponible. Procédez comme suit:

  1. Ouvrir IIS
  2. Développez Serveur puis ouvrez le dossier Sites
  3. Cliquez sur le site / l'application
  4. Accédez aux pages d'erreur
  5. Ouvrez l'élément d'état d'erreur 404 dans la liste
  6. Au lieu de l'option "Insérer le contenu du fichier statique dans la réponse d'erreur", remplacez-le par "Exécuter une URL sur ce site" et ajoutez une valeur de barre oblique "/" à l'URL.

Et cela fonctionnera désormais correctement.

entrez la description de l'image ici entrez la description de l'image ici

J'espère que ça aide. :-)


1

J'utilise WebPack, j'ai eu le même problème Solution => Dans votre fichier 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();
  });

Pourquoi mon application ne s'affiche-t-elle pas après avoir été actualisée?


Ça l'a fait pour moi! Au départ, j'avais res.sendFile (path.JOIN (publicPath, "index.html")); J'ai changé "joindre" en "résolu" comme dans l'exemple ci-dessus pour: res.sendFile (path.resolve ("./ dist", "index.html")); Je jouais aussi avec __dirname mais je ne pouvais pas vraiment le comprendre ou le faire fonctionner, alors j'ai copié manuellement dans "./dist" car c'est de là que mon index.html est servi. Je l'ai également déclaré comme précédemment: app.use (express.static ("./ dist"));
logixplayer

1

L'utilisation HashRouterde Redux a également fonctionné pour moi , il suffit de remplacer simplement:

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

J'ai eu ce même problème et cela solution a fonctionné pour nous ..

Contexte:

Nous hébergeons plusieurs applications sur le même serveur. Quand nous rafraîchirions le serveur ne comprendrait pas où chercher notre index dans le dossier dist pour cette application particulière. Le lien ci-dessus vous mènera à ce qui a fonctionné pour nous ... J'espère que cela vous aidera, car nous avons passé pas mal d'heures à trouver une solution à nos besoins.

Nous utilisons:

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"
}

mon 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]
}

mon 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')
)

mon 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

J'aime cette façon de le gérer. Essayez d'ajouter: yourSPAPageRoute / * côté serveur pour vous débarrasser de ce problème.

J'ai choisi cette approche car même l'API native HTML5 History ne prend pas en charge la redirection correcte lors de l'actualisation de la page (pour autant que je sache).

Remarque: la réponse sélectionnée a déjà répondu à cela, mais j'essaie d'être plus précis.

Route express

Test - API Historique Testé et je voulais juste partager cela.

J'espère que cela aide.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.