Gli URL di React-router non funzionano durante l'aggiornamento o la scrittura manuale


655

Sto usando React-router e funziona benissimo mentre faccio clic sui pulsanti di collegamento, ma quando aggiorno la mia pagina web non carica ciò che voglio.

Ad esempio, ci sto localhost/jobliste tutto va bene perché sono arrivato qui premendo un link. Ma se aggiorno la pagina web ottengo:

Cannot GET /joblist

Per impostazione predefinita, non ha funzionato in questo modo. Inizialmente ho avuto il mio URL, localhost/#/e localhost/#/joblisted hanno funzionato perfettamente bene. Ma non mi piace questo tipo di URL, quindi provando a cancellarlo #, ho scritto:

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

Questo problema non si verifica con localhost/, questo restituisce sempre ciò che voglio.

EDIT: questa app è a pagina singola, quindi /joblistnon è necessario chiedere nulla a nessun server.

EDIT2: il mio intero router.

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

a meno che non si usi htaccess per caricare la pagina principale del tour e dire al router di utilizzare location.pathname che non funzionerà ..
Charles John Thompson III

Come hai cancellato quel #simbolo? Grazie!
SudoPlz,

4
Se stai ospitando l'app di reazione in un bucket S3, puoi semplicemente impostare il documento di errore su index.html. Questo assicurerà che index.htmlvenga colpito, qualunque cosa accada.
Trevor Hutto,

Nel mio caso, funziona bene su Windows ma non su Linux
Ejaz Karim

Questo è il riferimento che mi ha aiutato a risolvere il mio problema: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

Risposte:


1096

Guardando i commenti sulla risposta accettata e la natura generica di questa domanda ("non funziona"), ho pensato che questo potesse essere un buon posto per alcune spiegazioni generali sui problemi qui coinvolti. Quindi questa risposta è intesa come informazione / elaborazione di sfondo sul caso d'uso specifico del PO. Per favore abbi pazienza.

Lato server vs lato client

La prima cosa importante da capire a riguardo è che ora ci sono 2 posti in cui viene interpretato l'URL, mentre in passato ne esisteva solo 1 nei "vecchi tempi". In passato, quando la vita era semplice, alcuni utenti inviavano una richiesta http://example.com/aboutal server, che controllava la parte del percorso dell'URL, stabiliva che l'utente stava richiedendo la pagina about e quindi restituiva quella pagina.

Con il routing lato client, che è ciò che React-Router fornisce, le cose sono meno semplici. Inizialmente, il client non ha ancora caricato alcun codice JS. Quindi la primissima richiesta sarà sempre al server. Ciò restituirà quindi una pagina che contiene i tag di script necessari per caricare React e React Router ecc. Solo quando tali script sono stati caricati inizia la fase 2. Nella fase 2, quando l'utente fa clic sul 'Chi siamo' link di navigazione, per esempio, l'URL è cambiato solo localmente a http://example.com/about(reso possibile dalla API Storia ), ma nessuna richiesta al server è fatta. Invece, React Router fa la sua parte sul lato client, determina quale vista React visualizzare e renderizza. Supponendo che la tua pagina di informazioni non debba effettuare chiamate REST, è già stata fatta. Sei passato da Home a Chi siamo senza che sia stata attivata alcuna richiesta del server.

Quindi, fondamentalmente, quando si fa clic su un collegamento, viene eseguito JavaScript che manipola l'URL nella barra degli indirizzi, senza causare un aggiornamento della pagina , che a sua volta fa sì che React Router esegua una transizione di pagina sul lato client .

Ma ora considera cosa succede se copi e incolli l'URL nella barra degli indirizzi e lo mandi via e-mail a un amico. Il tuo amico non ha ancora caricato il tuo sito web. In altre parole, è ancora nella fase 1 . Nessun React Router è ancora in esecuzione sulla sua macchina. Quindi il suo browser farà una richiesta al serverhttp://example.com/about .

Ed è qui che inizia il tuo problema. Fino ad ora, potresti cavartela semplicemente posizionando un HTML statico nella webroot del tuo server. Ma ciò darebbe 404errori per tutti gli altri URL quando richiesto dal server . Quegli stessi URL funzionano bene sul lato client , perché React Router sta eseguendo il routing per te, ma falliscono sul lato server a meno che tu non li capisca.

Combinazione di routing lato server e lato client

Se si desidera che l' http://example.com/aboutURL funzioni sia sul lato server che sul lato client, è necessario impostare percorsi per esso sia sul lato server che sul lato client. Ha senso giusto?

Ed è qui che iniziano le tue scelte. Le soluzioni vanno dal bypassare completamente il problema, attraverso un percorso generale che restituisce il bootstrap HTML, all'approccio isomorfo completo in cui sia il server che il client eseguono lo stesso codice JS.

.

Bypassare il problema del tutto: Hash History

Con Hash History anziché Browser History , il tuo URL per la pagina about sarebbe simile al seguente: http://example.com/#/about La parte dopo il #simbolo hash ( ) non viene inviata al server. Quindi il server vede http://example.com/e invia solo la pagina dell'indice come previsto. React-Router raccoglierà la #/aboutparte e mostrerà la pagina corretta.

Lati negativi :

  • URL "brutti"
  • Il rendering sul lato server non è possibile con questo approccio. Per quanto riguarda l'ottimizzazione per i motori di ricerca (SEO), il tuo sito web è costituito da una singola pagina con quasi nessun contenuto.

.

Catch-all

Con questo approccio si fa uso cronologia del browser, ma solo impostare un catch-all sul server che invia /*a index.html, in modo efficace dando più o meno la stessa situazione con Hash Storia. Hai comunque URL puliti e potresti migliorare questo schema in un secondo momento senza dover invalidare tutti i preferiti dell'utente.

Lati negativi :

  • Più complesso da configurare
  • Ancora nessun buon SEO

.

Ibrido

Nell'approccio ibrido espandi lo scenario generale aggiungendo script specifici per percorsi specifici. Potresti creare alcuni semplici script PHP per restituire le pagine più importanti del tuo sito con contenuti inclusi, in modo che Googlebot possa almeno vedere cosa c'è sulla tua pagina.

Lati negativi :

  • Ancora più complesso da configurare
  • Solo un buon SEO per quei percorsi che ti danno il trattamento speciale
  • Duplicazione del codice per il rendering del contenuto su server e client

.

Isomorfo

Che cosa succede se utilizziamo Node JS come nostro server in modo da poter eseguire lo stesso codice JS su entrambe le estremità? Ora, abbiamo tutti i nostri percorsi definiti in una singola configurazione di reazione-router e non abbiamo bisogno di duplicare il nostro codice di rendering. Questo è "il santo graal" per così dire. Il server invia lo stesso markup identico a quello che avremmo se la transizione della pagina fosse avvenuta sul client. Questa soluzione è ottimale in termini di SEO.

Lati negativi :

  • Il server deve (essere in grado di) eseguire JS. Ho sperimentato Java icw Nashorn ma non funziona per me. In pratica, ciò significa principalmente che è necessario utilizzare un server basato su Node JS.
  • Molte problematiche ambientali difficili (utilizzo windowsul lato server ecc.)
  • Ripida curva di apprendimento

.

Quale dovrei usare?

Scegli quello con cui puoi cavartela. Personalmente penso che il catch-all sia abbastanza semplice da impostare, quindi sarebbe il mio minimo. Questa configurazione ti consente di migliorare le cose nel tempo. Se stai già utilizzando Node JS come piattaforma server, indagherei sicuramente a fare un'app isomorfa. Sì, all'inizio è difficile, ma una volta capito è in realtà una soluzione molto elegante al problema.

Quindi sostanzialmente, per me, sarebbe questo il fattore decisivo. Se il mio server gira su Node JS, diventerei isomorfo; altrimenti sceglierei la soluzione Catch-all e la espanderei semplicemente (soluzione ibrida) man mano che il tempo avanza e i requisiti SEO lo richiedono.

Se vuoi saperne di più sul rendering isomorfo (chiamato anche 'universale') con React, ci sono alcuni buoni tutorial sull'argomento:

Inoltre, per iniziare, ti consiglio di guardare alcuni kit di partenza. Scegline uno che corrisponda alle tue scelte per lo stack tecnologico (ricorda, React è solo la V in MVC, hai bisogno di più cose per creare un'app completa). Inizia guardando quello pubblicato da Facebook stesso:

O scegli uno dei tanti dalla community. C'è un bel sito ora che cerca di indicizzarli tutti:

Ho iniziato con questi:

Attualmente sto usando una versione casalinga del rendering universale che è stata ispirata dai due kit di base sopra, ma ora non sono aggiornati.

Buona fortuna con la tua ricerca!


2
Ottimo post Stijn! Consiglieresti di andare con un kit di partenza per un'app di reazione isomorfa? In tal caso, potresti fare un esempio di quello che preferiresti?
Chris,

1
@ Paulos3000 Dipende dal server in uso. Fondamentalmente si definisce un percorso per /*e farlo rispondere con la tua pagina HTML. La cosa difficile qui è assicurarsi di non intercettare le richieste per i file .js e .css con questa route.
Stijn de Witt,

2
@ Paulos3000 Vedi qui per alcune domande correlate: per Apache / php , per Express / js , per J2E / Java .
Stijn de Witt,

1
Ciao, sto provando la soluzione di hash, comunque senza fortuna. Ottenere createMemoryHistory is not a functionerrore. Ti dispiace uno sguardo? stackoverflow.com/questions/45002573/…
Leon Gaban,

5
@LeonGaban Da quando è stata scritta questa risposta, React Router ha cambiato la sua implementazione. Ora hanno istanze Router diverse per le diverse storie e eseguono la configurazione della cronologia in background. I principi di base sono sempre gli stessi.
Stijn de Witt,

116

Le risposte qui sono tutte estremamente utili, ciò che ha funzionato per me è stata la configurazione del mio server Webpack per aspettarsi le rotte.

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

La storiaApiFallback è ciò che ha risolto questo problema per me. Ora il routing funziona correttamente e posso aggiornare la pagina o digitare direttamente l'URL. Non è necessario preoccuparsi di aggirare il problema sul server del nodo. Questa risposta ovviamente funziona solo se stai usando il webpack.

EDIT: vedi la mia risposta qui per un motivo più dettagliato per cui questo è necessario: https://stackoverflow.com/a/37622953/5217568


16
Si noti che il team Webpack consiglia di non utilizzare il server dev in produzione.
Stijn de Witt,

5
Per scopi di sviluppo generico questa è la soluzione migliore. historyApiFallbackè sufficiente. Come per tutte le altre opzioni, può anche essere impostato dalla CLI con il flag --history-api-fallback.
Marco Lazzeri,

2
@Kunok Non lo fa. Questa è una soluzione rapida per lo sviluppo, ma dovrai comunque capire qualcosa per la produzione.
Stijn de Witt,

contentbase: ': / perche' i file applicazione può essere l'accesso da l'url
Nezih

85

Puoi modificare il tuo .htaccessfile e inserirlo:

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

Sto usando react: "^16.12.0"e react-router: "^5.1.2" questo metodo è il Catch-all ed è probabilmente il modo più semplice per iniziare.


3
Funziona benissimo! e il più semplice se non vuoi ristrutturare la tua app
mhyassin,

7
Non dimenticare RewriteEngine On come prima riga
Kai Qing,

3
Nota che questa risposta si riferisce alla configurazione su un server Apache. Questo non fa parte di un'app React.
Colton Hicks,

Non ho capito le risposte sopra. Nessuno dei tutorriali ha detto che i miei link non funzionerebbero affatto dal vivo. Tuttavia, questo semplice file htaccess ha funzionato. Grazie
Thomas Williams il

Questo funziona anche per l'esportazione statica di next.js. Poiché tomcat non è simile al nodo, è necessaria questa configurazione aggiuntiva per l'esportazione statica next.js.
giri-jeedigunta

62

Per gli utenti di React Router V4 :

Se si tenta di risolvere questo problema con la tecnica Hash History menzionata in altre risposte, si noti che

<Router history={hashHistory} >

non funziona in V4, utilizzare HashRouterinvece:

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

<HashRouter>
  <App/>
</HashRouter>

Riferimento: HashRouter


Potresti fornire qualche dettaglio sul perché HashRouter risolve questo problema? Il link che hai fornito non me lo spiega. Inoltre, c'è un modo per nascondere l'hash nel percorso? Stavo usando BrowserRouter ma ho riscontrato questo problema 404 con esso ..
Ian Smith il

29

Il router può essere chiamato in due modi diversi, a seconda che la navigazione avvenga sul client o sul server. L'hai configurato per il funzionamento sul lato client. Il parametro chiave è il secondo per il metodo di esecuzione , la posizione.

Quando si utilizza il componente React Router Link, questo blocca la navigazione del browser e chiama la transizione per eseguire una navigazione sul lato client. Stai utilizzando HistoryLocation, quindi utilizza l'API della cronologia HTML5 per completare l'illusione della navigazione simulando il nuovo URL nella barra degli indirizzi. Se stai utilizzando browser meno recenti, questo non funzionerà. Dovresti utilizzare il componente HashLocation.

Quando si preme refresh, si ignora tutto il codice React e React Router. Il server riceve la richiesta /jobliste deve restituire qualcosa. Sul server è necessario passare il percorso richiesto al runmetodo affinché possa visualizzare la vista corretta. Puoi utilizzare la stessa mappa del percorso, ma probabilmente avrai bisogno di una chiamata diversa per Router.run. Come sottolinea Charles, puoi usare la riscrittura degli URL per gestirlo. Un'altra opzione è quella di utilizzare un server node.js per gestire tutte le richieste e passare il valore del percorso come argomento della posizione.

In express, ad esempio, potrebbe apparire così:

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

Si noti che il percorso della richiesta viene passato a run. Per fare ciò, è necessario disporre di un motore di visualizzazione lato server al quale è possibile passare l'HTML renderizzato. Ci sono molte altre considerazioni sull'uso renderToStringe nell'esecuzione di React sul server. Una volta che la pagina viene renderizzata sul server, quando l'app viene caricata nel client, verrà nuovamente visualizzata, aggiornando l'HTML renderizzato sul lato server secondo necessità.


2
scusa Mi scusi, potresti spiegare per favore la prossima frase "quando premi aggiorna, bypassa tutto il codice React e React Router"? Perché succede?
VB_

Il router React viene normalmente utilizzato per gestire diversi 'percorsi' solo all'interno del browser. Esistono due modi tipici per farlo: il percorso hash di stile precedente e l'API cronologia più recente. Un aggiornamento del browser farà una richiesta del server, che ignorerà il codice del router di reazione sul lato client. Dovrai gestire il percorso sul server (ma solo se stai utilizzando l'API della cronologia). Puoi usare il router di reazione per questo, ma dovrai fare qualcosa di simile a quello che ho descritto sopra.
Todd,

fatto. Ma come fare se il server è in un linguaggio non JS (diciamo Java)?
VB_

2
scusa ... vuoi dire che hai bisogno di un server express per rendere una route "non root"? Sono stato per la prima volta stupito che la definizione dell'isomorfismo non includesse il recupero dei dati nel suo concetto (le cose sono disperatamente complesse se si desidera visualizzare la vista CON il server dei dati, anche se sarebbe un'ovvia necessità SEO - e l'isomorfismo lo afferma). Ora sono come "WTF reagire", sul serio ... Correggimi se sbaglio, ember / angular / backbone richiedono una cosa server per eseguire il rendering di una route? Davvero non capisco questo obbligo gonfio di usare le rotte
Ben

1
Significa che l'aggiornamento in reag-router non funziona se la mia applicazione di reazione non è isomorfa?
Matt,

28

Nel tuo index.html head, aggiungi quanto segue:

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

Quindi, quando si esegue con il server dev webpack, utilizzare questo comando.

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

--history-api-fallback è la parte importante


Questo ha funzionato alla grande! Qualche link per saperne di più su come hai ottenuto / trovato quella soluzione?
justDan

1
Scusa, fratello. L'ho trovato attraverso la tecnica collaudata di tentativi ed errori.
Efe Ariaroo,

Vale la pena di ricordare che se si sta utilizzando a href di base che non è /, allora HashRouternon funziona (se si sta utilizzando hash di routing)
Robbie Averill

Il tag <base href = "/"> l'ha fatto per me. Grazie per questo. Il server di sviluppo Webpack ha continuato a provare a prendere il bundle dal percorso / <bundle> e non funzionava.
Malisbad,

28

Ho usato create-reazioni-app per creare un sito Web proprio ora e ho avuto lo stesso problema presentato qui. Uso BrowserRoutingdal react-router-dompacchetto. Sono in esecuzione su un server Nginx e ciò che ha risolto per me è stato l'aggiunta di quanto segue/etc/nginx/yourconfig.conf

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

Il che corrisponde all'aggiunta di quanto segue .htaccessnel caso in cui si esegue Appache

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

Anche questa sembra essere la soluzione suggerita da Facebook e può essere trovata qui


1
Caspita, una soluzione così semplice per nginx; funziona come un incantesimo, come nuovo utente nginx. Basta copiare e incollare e il gioco è fatto. +1 per il collegamento alla documentazione ufficiale fb. Ben fatto.
user3773048

Per la cronaca: sto usando un'istanza AWS con nginx e un'app angolare. Questo funziona
Diego Sarmiento,

mi hai salvato. stavo cercando di risolvere il mio problema da ieri mi sono stancato di soluzione ma non ci sono riuscito. finalmente mi hai aiutato a risolvere questo problema con il router. ah grazie mille fratello
Sharifur Robin,

C'è qualcosa di diverso necessario per "reagire": "^ 16.8.6", "reagire-router": "^ 5.0.1"? Ho provato queste soluzioni (sia nginx che apache) e non funzionano.
Mark A. Tagliaferro,

Dato che Facebook non ha modificato la sua documentazione al riguardo, posso solo supporre che la procedura sia la stessa. Non ci provo da un po 'però.
Aidin,

17

Questo può risolvere il tuo problema

Ho anche riscontrato lo stesso problema nell'applicazione ReactJS in modalità Produzione. Ecco la 2 soluzione al problema.

1.Cambia la cronologia di routing in "hashHistory" invece di browserHistory al posto di

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

Ora crea l'app usando il comando

sudo npm run build

Quindi posizionare la cartella di build nella cartella var / www /, ora l'applicazione funziona correttamente con l'aggiunta del tag # in ogni singolo URL. piace

localhost / # / home localhost / # / aboutus

Soluzione 2: senza # tag utilizzando browserHistory,

Imposta la tua cronologia = {browserHistory} nel tuo router, ora crealo usando sudo npm run build.

È necessario creare il file "conf" per risolvere la pagina 404 non trovata, il file conf dovrebbe essere così.

apri il tuo terminale digita i seguenti comandi

cd / etc / apache2 / sites-available ls nano sample.conf Aggiungi il contenuto qui sotto.

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

Ora devi abilitare il file sample.conf usando il seguente comando

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

quindi ti chiederà di ricaricare il server apache, usando il servizio sudo apache2 ricaricare o riavviare

quindi apri la cartella localhost / build e aggiungi il file .htaccess con il contenuto di seguito.

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

Ora l'app funziona normalmente.

Nota: cambia 0.0.0.0 ip nel tuo indirizzo IP locale.

In caso di dubbi in merito, sollevare un commento.

Spero sia utile per gli altri.


La prima soluzione funzionerà per "react-router-dom": "^5.1.2"?, Sto usando<BrowserRouter>
151291

16

Se stai ospitando un'app di reazione tramite AWS Static S3 Hosting & CloudFront

Questo problema si è presentato da CloudFront rispondendo con un messaggio Accesso negato 403 perché si aspettava che esistesse / un / altro / percorso nella mia cartella S3, ma quel percorso esiste solo internamente nell'instradamento di React con reagente-router.

La soluzione consisteva nell'impostare una regola di Pagine errori di distribuzione. Vai alle impostazioni di CloudFront e scegli la tua distribuzione. Quindi vai alla scheda "Pagine di errore". Fai clic su "Crea risposta errore personalizzata" e aggiungi una voce per 403 poiché è il codice di stato dell'errore che riceviamo. Imposta il percorso della pagina di risposta su /index.html e il codice di stato su 200. Il risultato finale mi stupisce per la sua semplicità. La pagina dell'indice viene pubblicata, ma l'URL viene conservato nel browser, quindi una volta caricata l'app di reazione, rileva il percorso dell'URL e naviga verso il percorso desiderato.

Regola errore pagine 403


1
Questo è esattamente ciò di cui avevo bisogno. Grazie.
Jonathan,

Quindi .. tutti i tuoi URL funzionano, ma restituisci il codice di stato 403? Non è corretto. I codici di stato previsti devono essere compresi nell'intervallo 2xx.
Stijn de Witt,

@StijndeWitt Non sono sicuro che tu capisca correttamente. CloudFront gestirà tutto: il client non vedrà alcun codice di stato 403. Il reinstradamento avviene sul lato server, esegue il rendering della pagina dell'indice, mantiene l'URL e, di conseguenza, fornisce il percorso corretto.
thmmorg

@ th3morg Ah sì, adesso lo vedo. Ho perso che ti consente anche di inserire il codice di stato della risposta come 200. Quindi, fondamentalmente, stai mappando lo stato 403 sullo stato 200 ... Sembra a posto ... tranne che forse se ottieni 403 come risultato a causa di altri motivi non potrai vederlo per questo.
Stijn de Witt,

1
grazie per questo @ th3morg. Questa configurazione funziona anche per percorsi multilivello? Funziona benissimo per me se qualsiasi percorso a livello di radice, come dominio / registrazione, dominio / accesso, dominio / <documentId>, ma non funziona per percorsi a più livelli, come dominio / documento / <documentId>.
Pargles,

16

Il Webpack Dev Server ha un'opzione per abilitare questo. Apri package.jsone aggiungi --history-api-fallback. Questa soluzione ha funzionato per me.

reagiscono-router dimostrativi


3
Va bene, webpack-dev-serverma come posso risolvere questo problema per la build di produzione?
Hussain,

12

Se stai ospitando l'app di reazione su IIS, aggiungi semplicemente un file web.config contenente:

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

Ciò dirà al server IIS di restituire la pagina principale al client invece dell'errore 404 e non è necessario utilizzare la cronologia hash.


grazie fratello !!!! è lavoro!
Thanh Bao

12

Aggiungi questo a webpack.config.js:

devServer: {
    historyApiFallback: true
}

È ottimo per gli sviluppatori, ma non aiuta con le build di produzione.
KFunk

11

Stack di produzione: React, React Router v4, BrowswerRouter, Express, Nginx

1) User BrowserRouter per URL belli

// app.js

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

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

2) Aggiungi index.html a tutte le richieste sconosciute utilizzando /*

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

4) eseguire nodemon server.jsonode server.js

EDIT: potresti voler lasciare che nginx gestisca questo nel blocco server e ignori il passaggio 2:

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

ottenere percorso di indefinito
Goutham

@Goutham Nella parte superiore del file server.js aggiungi import path from 'pathoconst path = require('path')
Isaac Pak

il passaggio 2 (cattura tutto con /*) mi ha appena salvato la giornata. Grazie! :)
Atlas7,

Questo funziona, per le persone che usano Redux e si preoccupano di un router connesso, vedi questo esempio: github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson

10

Se stai usando Crea app React:

C'è una grande passeggiata attraverso questo problema con le soluzioni per le principali piattaforme di hosting che puoi trovare QUI nella pagina Crea app React. Ad esempio, utilizzo React Router v4 e Netlify per il mio codice frontend. È bastato aggiungere 1 file alla mia cartella pubblica ("_redirects") e una riga di codice in quel file:

/*  /index.html  200

Ora il mio sito Web esegue correttamente il rendering di percorsi come mysite.com/pricing quando si accede al browser o quando qualcuno colpisce l'aggiornamento.


7

Prova ad aggiungere il file ".htaccess" nella cartella pubblica con il codice seguente.

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

RewriteRule ^ /index.html [L]  

Grazie, questo è ciò che ha funzionato per me su Fedora 28 con Apache. Nessuna delle regole di riscrittura di cui sopra né quelle nella pagina CRA hanno funzionato per me. Li ho aggiunti alla configurazione dell'host virtuale anziché in un file .htaccess separato.
boerre

6

Se hai un fallback nel tuo index.html, assicurati che nel tuo file index.html hai questo:

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

Ciò può differire da progetto a progetto.


2
Aggiungi questo al tuo html head: <base href = "/">
Efe Ariaroo

4

Se stai usando firebase tutto ciò che devi fare è assicurarti di avere una proprietà di riscrittura nel tuo file firebase.json nella radice della tua app (nella sezione hosting).

Per esempio:

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

Spero che questo salvi qualcun altro un accumulo di frustrazione e tempo perso.

Buona codifica ...

Ulteriori letture sull'argomento:

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

CLI Firebase: "Configura come app a pagina singola (riscrivi tutti gli URL in /index.html)"


Sei una leggenda
Perniferous,


3

Non sto ancora usando il rendering lato server ma ho riscontrato lo stesso problema dell'OP in cui Link sembrava funzionare bene per la maggior parte del tempo, ma fallivo quando avevo un parametro. Documenterò la mia soluzione qui per vedere se aiuta qualcuno.

Il mio jsx principale contiene questo:

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

Funziona bene per il primo collegamento corrispondente ma quando l'id: cambia nelle <Link>espressioni nidificate nella pagina dei dettagli di quel modello, l'URL cambia nella barra del browser ma il contenuto della pagina inizialmente non cambia per riflettere il modello collegato.

Il problema era che avevo usato il props.params.idper impostare il modello componentDidMount. Il componente viene appena montato una volta, quindi questo significa che il primo modello è quello che si attacca alla pagina e i Link successivi cambiano i puntelli ma lasciano la pagina invariata.

L'impostazione del modello nello stato del componente in entrambi componentDidMounte in componentWillReceiveProps(dove si basa sui puntelli successivi) risolve il problema e il contenuto della pagina cambia per riflettere il modello desiderato.


1
Potrebbe essere preferibile utilizzare il costruttore del componente (che ha anche accesso a props) iso componentDidMountse si desidera provare per il rendering sul lato server. Perché componentDidMountviene chiamato solo nel browser. Lo scopo è quello di fare cose con il DOM, come collegare ascoltatori di eventi a bodyecc. In cui non è possibile eseguire render.
Stijn de Witt,

3

Questo argomento è un po 'vecchio e risolto, ma vorrei suggerirti una soluzione semplice, chiara e migliore. Funziona se si utilizza il server Web.

Ogni server Web ha la possibilità di reindirizzare l'utente a una pagina di errore in caso di http 404. Per risolvere questo problema è necessario reindirizzare l'utente alla pagina di indice.

Se si utilizza il server di base Java (tomcat o qualsiasi server di applicazioni java) la soluzione potrebbe essere la seguente:

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>

Esempio:

  • OTTIENI http://example.com/about
  • Il server Web genera http 404 perché questa pagina non esiste sul lato server
  • la configurazione della pagina di errore indica al server che invia la pagina index.jsp all'utente
  • quindi JS farà il resto del lavoro sul lato client perché l'URL sul lato client è ancora http://example.com/about .

Ecco, non servono più magie :)


È stato fantastico! Sto usando il server Wildfly 10.1 e ho fatto questo aggiornamento al mio file web.xml, tranne per il fatto che la mia posizione era impostata su '/'. Questo perché nel mio codice di reazione ho usato la cronologia del browser in questo modo:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L

1
Sei sicuro che ciò non influisca sul SEO? Stai dando uno stato 404 alle pagine che esistono realmente. L'utente potrebbe non accorgersene mai, ma i robot prestano molta attenzione, in effetti così tanto, che non rascheranno la tua pagina.
subharb il

3

Se si utilizza Express o qualche altro framework nel back-end, è possibile aggiungere la configurazione simile come di seguito e controllare il percorso pubblico Webpack nella configurazione, dovrebbe funzionare bene anche al ricaricamento se si utilizza BrowserRouter

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

questa è la soluzione più semplice. notare che questo percorso dovrebbe andare dopo tutti gli altri percorsi, come si tratta di un fermo tutto
dcsan

3

Correzione dell'errore "impossibile ottenere / URL" all'aggiornamento o alla chiamata diretta dell'URL.

Configura il tuo webpack.config.js in modo che il link specificato installi i percorsi in questo modo.

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

2

Mentre sto usando .Net Core MVC qualcosa del genere mi ha aiutato:

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

Fondamentalmente nel lato MVC, tutti i percorsi che non corrispondono cadranno Home/Indexcome specificato startup.cs. Al Indexsuo interno è possibile ottenere l'URL della richiesta originale e passarlo dove necessario.

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

Se l'API Web è separata dall'app, come gestirla?
dayanrr91,

@ dayanrr91 se il tuo progetto ha API Web, non avrai comunque bisogno del routing lato server. E credo che il tuo reagente router dovrebbe leggere l'URL e fare il routing appropriato. Niente dovrebbe bloccarlo
Elnoor

il tuo commento è molto apprezzato, ma poi ho una domanda, ho appena aggiunto HashRouter, ma ora quando accedo a qualsiasi URL manualmente reindirizzerà al mio componente home invece di reindirizzare a casa mia e quindi al componente che stavo cercando di entrando, c'è qualche soluzione per questo?
dayanrr91,

@ dayanrr91 nessuna idea davvero, non ho mai provato l'hashrouter ma penso che dovrebbe avere qualcosa a che fare con il tuo codice. Il modo in cui funziona l'hashrouter dovrebbe essere simile al browserrouter. Inoltre ho appena testato qui in questo sandbox, funziona bene codesandbox.io/s/25okp1mny , testare l'URL 25okp1mny.codesandbox.io/#/roster/5
Elnoor

2

Se stai ospitando in IIS; L'aggiunta di questo al mio webconfig ha risolto il mio problema

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

È possibile effettuare una configurazione simile per qualsiasi altro server


1

Nel caso, qualcuno è qui alla ricerca di una soluzione su React JS SPA con Laravel. La risposta accettata è la migliore spiegazione del perché si verifichino tali problemi. Come già spiegato, è necessario configurare sia il lato client che il lato server. Nel modello di lama, includere il file in bundle js, assicurarsi di utilizzare in URL facadequesto modo

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

Nei percorsi, assicurati di aggiungerlo all'endpoint principale in cui si trova il modello di blade. Per esempio,

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

Quanto sopra è l'endpoint principale per il modello di lama. Ora aggiungi anche un percorso opzionale,

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

Il problema che si verifica è che prima viene caricato il modello di blade, quindi il router di reazione. Quindi, durante il caricamento '/setting-alerts', carica l'html e il js. Ma quando si carica '/setting-alerts/about', si carica prima sul lato server. Dal momento che sul lato server, non c'è nulla in questa posizione, restituisce non trovato. Quando si dispone di quel router opzionale, carica quella stessa pagina e viene caricato anche il router di reazione, quindi il caricatore di reazione decide quale componente mostrare. Spero che sia di aiuto.


È possibile caricare un URI esatto sul server, quindi il server effettua un reindirizzamento a React? Ad esempio, quando carico /user/{userID}, ho solo bisogno di restituire una /uservista HTML universale , portare l'ID e chiamarlo utilizzando AJAX o axios. è possibile farlo? sono SEO friendly?
Ryuujo,

1

Per coloro che utilizzano IIS 10, questo è ciò che dovresti fare per renderlo giusto. Assicurati di utilizzare browserHistory con questo. Come riferimento fornirò il codice per il routing, ma questo non è ciò che conta, ciò che conta è il passaggio successivo dopo il codice componente di seguito:

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

Poiché il problema è che IIS riceve la richiesta dai browser client, interpreterà l'URL come se stesse chiedendo una pagina, quindi restituisce una pagina 404 poiché non è disponibile alcuna pagina. Fare quanto segue:

  1. Apri IIS
  2. Espandere Server quindi aprire la cartella Siti
  3. Fai clic sul sito Web / sull'applicazione
  4. Vai alle pagine di errore
  5. Apri la voce di stato dell'errore 404 nell'elenco
  6. Invece dell'opzione "Inserisci contenuto dal file statico nella risposta dell'errore", modificalo in "Esegui un URL su questo sito" e aggiungi il valore "/" alla barra.

E ora funzionerà bene.

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Spero possa essere d'aiuto. :-)


1

Sto usando WebPack, ho avuto lo stesso problema Soluzione => Nel tuo file 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();
  });

Perché la mia applicazione non viene visualizzata dopo l'aggiornamento?


Questo l'ha fatto per me! Inizialmente avevo res.sendFile (path.JOIN (publicPath, "index.html")); Ho cambiato "join" in "risolto" come nell'esempio precedente in: res.sendFile (path.resolve ("./ dist", "index.html")); Stavo anche giocando con __dirname ma non riuscivo davvero a capirlo o farlo funzionare, quindi ho copiato manualmente in "./dist" perché è qui che viene fornito il mio index.html. L'ho anche dichiarato così prima: app.use (express.static ("./ dist"));
logixplayer

1

Usando ha HashRouterfunzionato anche per me con Redux , basta semplicemente sostituire:

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

per:

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

Ho avuto lo stesso problema e questa soluzione ha funzionato per noi ..

Sfondo:

Stiamo ospitando più app sullo stesso server. Quando aggiorneremo il server non capirà dove cercare il nostro indice nella cartella dist per quella particolare app. Il link sopra ti porterà a ciò che ha funzionato per noi ... Spero che questo ci aiuti, dato che abbiamo impiegato parecchie ore a trovare una soluzione per le nostre esigenze.

Stiamo usando:

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

il mio 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]
}

il mio 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')
)

my 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

Mi piace questo modo di gestirlo. Prova ad aggiungere: yourSPAPageRoute / * sul lato server per eliminare questo problema.

Ho seguito questo approccio perché anche l'API della cronologia HTML5 nativa non supporta il reindirizzamento corretto sull'aggiornamento della pagina (per quanto ne so).

Nota: la risposta selezionata ha già risolto questo problema, ma sto cercando di essere più specifico.

Express Route

Test - API History Testato e volevo solo condividere questo.

Spero che sia d'aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.