Las URL del enrutador de reacción no funcionan al actualizar o escribir manualmente

655

Estoy usando React-router y funciona bien mientras hago clic en los botones de enlace, pero cuando actualizo mi página web no carga lo que quiero.

Por ejemplo, estoy dentro localhost/joblisty todo está bien porque llegué aquí presionando un enlace. Pero si actualizo la página web obtengo:

Cannot GET /joblist

Por defecto, no funcionó así. Inicialmente tenía mi URL como localhost/#/y localhost/#/joblistfuncionaron perfectamente bien. Pero no me gusta este tipo de URL, así que tratando de borrar eso #, escribí:

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

Este problema no ocurre con localhost/, este siempre devuelve lo que quiero.

EDITAR: Esta aplicación es de una sola página, por lo /joblistque no necesita preguntar nada a ningún servidor.

EDIT2: todo mi enrutador.

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);
});
DavidDev
fuente
a menos que use htaccess para cargar su página principal de turismo y decirle a su enrutador que use location.pathname no funcionará ...
Charles John Thompson III
¿Cómo borraste ese #símbolo? ¡Gracias!
SudoPlz
44
Si aloja su aplicación de reacción en un bucket de S3, simplemente puede configurar el documento de error en index.html. Esto asegurará que index.htmlsea ​​golpeado pase lo que pase.
Trevor Hutto
En mi caso, funciona bien en Windows pero no en Linux
Ejaz Karim
Esta es la referencia que ayudó a resolver mi problema: github.com/facebook/create-react-app/blob/master/packages/…
jimbotron

Respuestas:

1096

Al observar los comentarios sobre la respuesta aceptada y la naturaleza genérica de esta pregunta ('no funciona'), pensé que este podría ser un buen lugar para algunas explicaciones generales sobre los problemas involucrados aquí. Por lo tanto, esta respuesta pretende ser información / elaboración de antecedentes sobre el caso de uso específico del OP. Por favor, tenga paciencia conmigo.

Lado del servidor vs lado del cliente

Lo primero que hay que entender sobre esto es que ahora hay 2 lugares donde se interpreta la URL, mientras que solía haber solo 1 en 'los viejos tiempos'. En el pasado, cuando la vida era simple, algún usuario enviaba una solicitud http://example.com/aboutal servidor, que inspeccionaba la parte de la ruta de acceso de la URL, determinaba que el usuario estaba solicitando la página acerca de y luego la devolvía.

Con el enrutamiento del lado del cliente, que es lo que proporciona React-Router, las cosas son menos simples. Al principio, el cliente aún no tiene ningún código JS cargado. Por lo tanto, la primera solicitud siempre será al servidor. Eso devolverá una página que contiene las etiquetas de secuencia de comandos necesarias para cargar React y React Router, etc. Solo cuando se hayan cargado esas secuencias de comandos comienza la fase 2. En la fase 2, cuando el usuario hace clic en el enlace de navegación 'Acerca de nosotros', por ejemplo, la URL se cambia localmente solo a http://example.com/about(posible mediante la API de historial ), pero no se realiza ninguna solicitud al servidor. En cambio, React Router hace lo suyo en el lado del cliente, determina qué vista de React renderizar y lo representa. Asumiendo que su página acerca de no necesita hacer llamadas REST, ya está hecha. Ha pasado de Inicio a Acerca de nosotros sin que se haya activado ninguna solicitud del servidor.

Básicamente, cuando hace clic en un enlace, se ejecuta un Javascript que manipula la URL en la barra de direcciones, sin causar una actualización de la página , lo que a su vez hace que React Router realice una transición de página en el lado del cliente .

Pero ahora considere lo que sucede si copia y pega la URL en la barra de direcciones y se la envía por correo electrónico a un amigo. Tu amigo aún no ha cargado tu sitio web. En otras palabras, ella todavía está en la fase 1 . Todavía no se está ejecutando ningún React Router en su máquina. Entonces su navegador hará una solicitud al servidorhttp://example.com/about .

Y aquí es donde comienza tu problema. Hasta ahora, podría salirse con la simple colocación de un HTML estático en la raíz web de su servidor. Pero eso daría 404errores para todas las otras URL cuando se solicite desde el servidor . Esas mismas URL funcionan bien en el lado del cliente , porque React Router está haciendo el enrutamiento por usted, pero fallan en el lado del servidor a menos que haga que su servidor las entienda.

Combinación de enrutamiento del lado del servidor y del cliente

Si desea que la http://example.com/aboutURL funcione tanto en el lado del servidor como en el lado del cliente, debe configurar rutas para ella tanto en el lado del servidor como en el del cliente. Tiene sentido ¿verdad?

Y aquí es donde comienzan tus elecciones. Las soluciones van desde eludir el problema por completo, a través de una ruta general que devuelve el HTML de arranque, hasta el enfoque isomorfo completo en el que tanto el servidor como el cliente ejecutan el mismo código JS.

.

Evitando el problema por completo: Hash History

Con Hash History en lugar de Browser History , su URL para la página acerca se vería así: http://example.com/#/about La parte que #sigue al símbolo hash ( ) no se envía al servidor. Por lo tanto, el servidor solo ve http://example.com/y envía la página de índice como se esperaba. React-Router recogerá la #/aboutparte y mostrará la página correcta.

Desventajas :

  • URL 'feas'
  • La representación del lado del servidor no es posible con este enfoque. En lo que respecta a la optimización de motores de búsqueda (SEO), su sitio web consta de una sola página con casi ningún contenido.

.

Catch-all

Con este enfoque, usted usa el Historial del navegador, pero simplemente configura un servidor general que envía /*a index.html, lo que le proporciona la misma situación que con el Historial de hash. Sin embargo, tiene URL limpias y podría mejorar este esquema más adelante sin tener que invalidar todos los favoritos de sus usuarios.

Desventajas :

  • Más complejo de configurar
  • Todavía no hay buen SEO

.

Híbrido

En el enfoque híbrido, se expande en el escenario general al agregar scripts específicos para rutas específicas. Puede crear algunos scripts PHP simples para devolver las páginas más importantes de su sitio con contenido incluido, de modo que Googlebot pueda al menos ver lo que hay en su página.

Desventajas :

  • Aún más complejo de configurar
  • Solo buen SEO para esas rutas le das el trato especial
  • Duplicación de código para representar contenido en el servidor y el cliente

.

Isomorfo

¿Qué sucede si utilizamos Node JS como nuestro servidor para poder ejecutar el mismo código JS en ambos extremos? Ahora, tenemos todas nuestras rutas definidas en una única configuración de enrutador de reacción y no necesitamos duplicar nuestro código de representación. Este es 'el santo grial', por así decirlo. El servidor envía exactamente el mismo marcado que terminaríamos si la transición de página hubiera ocurrido en el cliente. Esta solución es óptima en términos de SEO.

Desventajas :

  • Servidor debe (poder) ejecutar JS. He experimentado con Java icw Nashorn pero no me funciona. En la práctica, significa principalmente que debe utilizar un servidor basado en Node JS.
  • Muchos problemas ambientales difíciles (usando window en el lado del servidor, etc.)
  • Curva de aprendizaje empinada

.

¿Cuál debería usar?

Elija el que pueda salirse con la suya. Personalmente, creo que el conjunto es lo suficientemente simple como para configurarlo, por lo que ese sería mi mínimo. Esta configuración le permite mejorar las cosas con el tiempo. Si ya está utilizando Node JS como su plataforma de servidor, definitivamente investigaría hacer una aplicación isomórfica. Sí, es difícil al principio, pero una vez que lo dominas, en realidad es una solución muy elegante para el problema.

Básicamente, para mí, ese sería el factor decisivo. Si mi servidor se ejecuta en el Nodo JS, me volvería isomorfo; de lo contrario, optaría por la solución Catch-all y solo la ampliaría (solución híbrida) a medida que pasa el tiempo y los requisitos de SEO lo exigen.

Si desea obtener más información sobre el procesamiento isomorfo (también llamado 'universal') con React, hay algunos buenos tutoriales sobre el tema:

Además, para comenzar, te recomiendo mirar algunos kits de inicio. Elija uno que coincida con sus opciones para la pila de tecnología (recuerde, React es solo la V en MVC, necesita más cosas para construir una aplicación completa). Comience mirando el publicado por el propio Facebook:

O elija uno de los muchos de la comunidad. Hay un buen sitio ahora que intenta indexarlos a todos:

Empecé con estos:

Actualmente estoy usando una versión casera de renderizado universal inspirada en los dos kits de inicio anteriores, pero ahora están desactualizados.

¡Buena suerte con tu búsqueda!

Stijn de Witt
fuente
2
Gran post Stijn! ¿Recomendaría ir con un kit de inicio para una aplicación de reacción Isomorphic? Si es así, ¿podría dar un ejemplo de uno que preferiría?
Chris
1
@ Paulos3000 Depende del servidor que esté utilizando. Básicamente, usted define una ruta /*y hace que responda con su página HTML. Lo complicado aquí es asegurarse de no interceptar las solicitudes de los archivos .js y .css con esta ruta.
Stijn de Witt
2
@ Paulos3000 Vea aquí algunas preguntas relacionadas: para Apache / php , para Express / js , para J2E / Java .
Stijn de Witt
1
Hola, estoy probando la solución hash, sin embargo, no tuve suerte. Obteniendo createMemoryHistory is not a functionerror. ¿Te importa mirar? stackoverflow.com/questions/45002573/…
Leon Gaban
55
@LeonGaban Parece que desde que se escribió esta respuesta, React Router cambió su implementación. Ahora tienen diferentes instancias de enrutador para los diferentes historiales y realizan la configuración del historial en segundo plano. Los principios básicos siguen siendo los mismos.
Stijn de Witt
116

Las respuestas aquí son extremadamente útiles, lo que funcionó para mí fue configurar mi servidor Webpack para esperar las rutas.

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

El historyApiFallback es lo que solucionó este problema para mí. Ahora el enrutamiento funciona correctamente y puedo actualizar la página o escribir la URL directamente. No necesita preocuparse por las soluciones en su servidor de nodo. Esta respuesta obviamente solo funciona si estás usando webpack.

EDITAR: vea mi respuesta aquí para obtener una razón más detallada de por qué esto es necesario: https://stackoverflow.com/a/37622953/5217568

jmancherje
fuente
16
Tenga en cuenta que el equipo de Webpack recomienda no usar el servidor de desarrollo en producción.
Stijn de Witt
55
Para fines de desarrollo genérico, esta es la mejor solución. historyApiFallbackes suficiente. En cuanto a todas las demás opciones, también se puede configurar desde CLI con el indicador --history-api-fallback.
Marco Lazzeri
2
@Kunok No lo hace. Esta es una solución rápida para el desarrollo, pero aún tendrá que resolver algo para la producción.
Stijn de Witt
contentBase: ': /' porque se puede acceder a los archivos de su aplicación desde la url
Nezih
85

Puede cambiar su .htaccessarchivo e insertar esto:

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

Estoy usando react: "^16.12.0"y react-router: "^5.1.2" este método es el Catch-all y es probablemente la forma más fácil de comenzar.

BrahimS
fuente
3
Esto funciona bien! y lo más fácil si no quieres reestructurar tu aplicación
mhyassin
77
No olvides RewriteEngine On como primera línea
Kai Qing
3
Tenga en cuenta que esta respuesta se refiere a la configuración en un servidor Apache. Esto no es parte de una aplicación React.
Colton Hicks
No entendí las respuestas anteriores. Ninguno de los tutoriales dijo que mis enlaces no funcionarían en vivo. Sin embargo, este simple archivo htaccess funcionó. Gracias
Thomas Williams
Esto también funciona para la exportación estática next.js. Como el tomcat no es similar al nodo, necesitamos esta configuración adicional para la exportación estática next.js.
giri-jeedigunta
62

Para usuarios de React Router V4 :

Si intenta resolver este problema mediante la técnica Hash History mencionada en otras respuestas, tenga en cuenta que

<Router history={hashHistory} >

no funciona en V4, utilice HashRouter en lugar:

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

<HashRouter>
  <App/>
</HashRouter>

Referencia: HashRouter

usuario2875289
fuente
¿Podría dar algún detalle de por qué el HashRouter soluciona este problema? El enlace que proporcionó no me lo explica. Además, ¿hay alguna forma de ocultar el hash en el camino? Estaba usando el BrowserRouter pero encontré este problema 404 con él ..
Ian Smith
29

Se puede llamar al enrutador de dos maneras diferentes, dependiendo de si la navegación se produce en el cliente o en el servidor. Lo tiene configurado para la operación del lado del cliente. El parámetro clave es el segundo del método de ejecución. , la ubicación.

Cuando usa el componente React Router Link, bloquea la navegación del navegador y llama a la transición a para hacer una navegación del lado del cliente. Está utilizando HistoryLocation, por lo que utiliza la API de historial HTML5 para completar la ilusión de navegación simulando la nueva URL en la barra de direcciones. Si está utilizando navegadores antiguos, esto no funcionará. Debería usar el componente HashLocation.

Cuando presiona actualizar, omite todo el código React y React Router. El servidor recibe la solicitud /joblisty debe devolver algo. En el servidor, debe pasar la ruta solicitada al runmétodo para que presente la vista correcta. Puede usar el mismo mapa de ruta, pero probablemente necesitará una llamada diferente paraRouter.run . Como señala Charles, puede usar la reescritura de URL para manejar esto. Otra opción es usar un servidor node.js para manejar todas las solicitudes y pasar el valor de la ruta como argumento de ubicación.

En express, por ejemplo, podría verse así:

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

Tenga en cuenta que la ruta de solicitud se pasa a run. Para hacer esto, necesitará tener un motor de vista del lado del servidor al que pueda pasar el HTML renderizado. Hay una serie de otras consideraciones al usar renderToStringy ejecutar React en el servidor. Una vez que la página se procesa en el servidor, cuando su aplicación se carga en el cliente, se procesará nuevamente, actualizando el HTML procesado del lado del servidor según sea necesario.

Todd
fuente
2
disculpe, disculpe, ¿puede explicar por favor la siguiente declaración "cuando presiona actualizar, omite todo el código React y React Router"? ¿Por que sucede?
VB_
El enrutador React se usa normalmente para manejar diferentes 'rutas' dentro del navegador solamente. Hay dos formas típicas de hacer esto: la ruta de hash de estilo anterior y la API de historial más reciente. Una actualización del navegador realizará una solicitud del servidor, que omitirá el código del enrutador de reacción del lado del cliente. Deberá manejar la ruta en el servidor (pero solo si está utilizando la API de historial). Puede usar el enrutador de reacción para esto, pero deberá hacer algo similar a lo que describí anteriormente.
Todd
Entendido. Pero, ¿cómo ser si el servidor está en un lenguaje que no es JS (digamos Java)?
VB_
2
lo siento ... ¿quiere decir que necesita un servidor express para representar una ruta que es "no root"? Primero me sorprendió que la definición del isomorfismo no incluyera la búsqueda de datos en su concepto (las cosas son desesperadamente complejas si quieres renderizar la vista CON el servidor de datos, aunque sería una obvia necesidad de SEO, y el isomorfismo lo reclama). Ahora estoy como "WTF reaccionar", en serio ... Corrígeme si me equivoco, ¿ember / angular / backbone requiere un servidor para representar una ruta? Realmente no entiendo este requisito inflado para usar rutas
Ben
1
¿Significa que la actualización en react-router no funciona si mi aplicación de reacción no es isomórfica?
Matt
28

En su index.html head, agregue lo siguiente:

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

Luego, cuando se ejecuta con el servidor de desarrollo webpack, use este comando.

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

--history-api-fallback es la parte importante

Efe Ariaroo
fuente
Esto funcionó muy bien! ¿Algún enlace para leer un poco más sobre cómo obtuviste / encontraste esa solución?
justDan
1
Lo siento hermano. Lo encontré a través de la técnica probada y verdadera de prueba y error.
Efe Ariaroo
Vale la pena mencionar que si está utilizando un href base que no funciona, /entonces HashRouterno funcionará (si está utilizando enrutamiento hash)
Robbie Averill
La etiqueta <base href = "/"> lo hizo por mí. Gracias por esto. El servidor de desarrollo de Webpack seguía intentando tomar el paquete de la ruta / <paquete> y fallaba.
Malisbad
28

Utilicé create-react-app para hacer un sitio web en este momento y tuve el mismo problema presentado aquí. Yo uso BrowserRoutingdel react-router-dompaquete. Estoy corriendo en un servidor Nginx y lo que me resolvió fue agregar lo siguiente a/etc/nginx/yourconfig.conf

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

Lo que corresponde a agregar lo siguiente .htaccessen caso de que esté ejecutando Appache

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

Esta también parece ser la solución sugerida por Facebook y se puede encontrar aquí.

Ayuda en
fuente
1
Wow, una solución tan simple para nginx; funciona como un encanto, como un nuevo usuario de nginx. Simplemente copie y pegue y listo. +1 para vincular a la documentación oficial de fb. Camino a seguir.
user3773048
Para el registro: estoy usando una instancia de AWS con nginx y una aplicación angular. Esto funciona.
Diego Sarmiento
me salvas. Estaba tratando de solucionar mi problema desde ayer. Cansé un par de soluciones pero fallé. finalmente me ayudaste a resolver este problema del enrutador. ah muchas gracias hermano
Sharifur Robin
¿Se necesita algo diferente para "reaccionar": "^ 16.8.6", "react-router": "^ 5.0.1"? Probé estas soluciones (tanto nginx como apache) y no funcionan.
Mark A. Tagliaferro
Como Facebook no ha cambiado su documentación al respecto, solo puedo suponer que el procedimiento es el mismo. Sin embargo, no lo he probado en mucho tiempo.
Aidin
17

Esto puede resolver tu problema

También enfrenté el mismo problema en la aplicación ReactJS en modo Producción. Aquí está la 2 solución al problema.

1.Cambie el historial de enrutamiento a "hashHistory" en lugar de browserHistory en lugar de

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

Ahora compila la aplicación usando el comando

sudo npm run build

Luego coloque la carpeta de compilación en su carpeta var / www /. Ahora la aplicación está funcionando bien con la adición de la etiqueta # en cada una de las URL. me gusta

localhost / # / home localhost / # / aboutus

Solución 2: Sin la etiqueta # usando browserHistory,

Establezca su historial = {browserHistory} en su enrutador, ahora compílelo usando sudo npm run build.

Debe crear el archivo "conf" para resolver la página 404 no encontrada, el archivo conf debería ser así.

abre tu terminal escribe los siguientes comandos

cd / etc / apache2 / sites-available ls nano sample.conf Agregue el contenido a continuación.

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

Ahora debe habilitar el archivo sample.conf utilizando el siguiente comando

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

luego le pedirá que vuelva a cargar el servidor apache, utilizando el servicio sudo apache2 reload o restart

luego abra su carpeta localhost / build y agregue el archivo .htaccess con el contenido de abajo.

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

Ahora la aplicación funciona normalmente.

Nota: cambie 0.0.0.0 ip a su dirección IP local.

Si tiene alguna duda al respecto, no dude en hacer un comentario.

Espero que sea útil para los demás.

Venkatesh Somu
fuente
¿ "react-router-dom": "^5.1.2"<BrowserRouter>
Funcionará la
16

Si aloja una aplicación de reacción a través de AWS Static S3 Hosting y CloudFront

Este problema se presentó cuando CloudFront respondió con un mensaje 403 de acceso denegado porque esperaba que / some / other / path existiera en mi carpeta S3, pero esa ruta solo existe internamente en el enrutamiento de React con react-router.

La solución fue configurar una regla de páginas de error de distribución. Vaya a la configuración de CloudFront y elija su distribución. A continuación, vaya a la pestaña "Páginas de error". Haga clic en "Crear respuesta de error personalizada" y agregue una entrada para 403 ya que ese es el código de estado de error que obtenemos. Establezca la Ruta de la página de respuesta en /index.html y el código de estado en 200. El resultado final me sorprende con su simplicidad. La página de índice se sirve, pero la URL se conserva en el navegador, por lo que una vez que se carga la aplicación de reacción, detecta la ruta de la URL y navega hacia la ruta deseada.

Error Páginas 403 Regla

th3morg
fuente
1
Esto es exactamente lo que necesitaba. Gracias.
Jonathan
Entonces ... ¿todas sus URL funcionan, pero devuelven el código de estado 403? No es correcto. Los códigos de estado esperados deben estar en el rango 2xx.
Stijn de Witt
@StijndeWitt No estoy seguro de que esté entendiendo correctamente. CloudFront se encargará de todo: el cliente no verá ningún código de estado 403. El cambio de ruta se realiza en el lado del servidor, representa la página de índice, mantiene la URL y, como resultado, proporciona la ruta correcta.
th3morg
@ th3morg Ah sí, ya veo ahora. Me perdí que también le permite completar el código de estado de respuesta como 200. Entonces, básicamente, está asignando el estado 403 al estado 200 ... Parece bien ... excepto que tal vez si obtiene 403 como resultado debido a alguna otra razón no podrás verlo debido a esto.
Stijn de Witt
1
gracias por esto @ th3morg. ¿Esta configuración también funciona para rutas de varios niveles? Funciona muy bien para mí si hay alguna ruta en el nivel raíz, como dominio / registro, dominio / inicio de sesión, dominio / <documentId>, pero no funciona para rutas de varios niveles, como dominio / documento / <documentId>.
Pargles
16

El Webpack Dev Server tiene una opción para habilitar esto. Abre package.jsony agrega --history-api-fallback. Estas soluciones me funcionaron.

reaccionar-enrutador-tutorial

srijishks
fuente
3
Está bien, webpack-dev-serverpero ¿cómo soluciono esto para la compilación de producción?
Hussain
12

Si aloja su aplicación de reacción en IIS, simplemente agregue un archivo web.config que contenga:

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

Esto le indicará al servidor IIS que devuelva la página principal al cliente en lugar del error 404 y no es necesario usar el historial de hash.

Chtiwi Malek
fuente
¡¡¡¡gracias hermano!!!! ¡es trabajo!
Thanh Bao
12

Agregue esto a webpack.config.js:

devServer: {
    historyApiFallback: true
}
suraj
fuente
Eso es genial para los desarrolladores, pero no ayuda con las compilaciones de producción.
KFunk
11

Pila de producción: React, React Router v4, BrowswerRouter, Express, Nginx

1) User BrowserRouter para urls bonitas

// app.js

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

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

2) Agregue index.html a todas las solicitudes desconocidas utilizando /*

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

4) correr nodemon server.jsonode server.js

EDITAR: es posible que desee dejar que nginx maneje esto en el bloque del servidor y no tenga en cuenta el paso 2:

location / {
    try_files $uri /index.html;
}
Isaac Pak
fuente
conseguir camino de indefinido
Goutham
@Goutham En la parte superior de su archivo server.js agregue import path from 'pathoconst path = require('path')
Isaac Pak
el paso 2 (atrapar a todos con /*) acaba de salvarme el día. ¡Gracias! :)
Atlas7
Esto funciona, para las personas que usan redux y se preocupan por un enrutador conectado, vea este ejemplo: github.com/supasate/connected-react-router/tree/master/examples/…
cjjenkinson
10

Si está utilizando la aplicación Create React:

Sin embargo, hay un gran recorrido por este problema con soluciones para muchas de las principales plataformas de alojamiento que puede encontrar AQUÍ en la página Crear aplicación React. Por ejemplo, uso React Router v4 y Netlify para mi código de interfaz. Todo lo que se necesitó fue agregar 1 archivo a mi carpeta pública ("_redirects") y una línea de código en ese archivo:

/*  /index.html  200

Ahora mi sitio web representa correctamente rutas como mysite.com/pricing cuando se ingresa al navegador o cuando alguien presiona actualizar.

Colton Hicks
fuente
7

Intente agregar el archivo ".htaccess" dentro de la carpeta pública con el siguiente código.

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

RewriteRule ^ /index.html [L]  
Kiran Joshi
fuente
Gracias, esto fue lo que funcionó para mí en Fedora 28 con apache. Ninguna de las reglas de reescritura anteriores ni las de la página de CRA funcionaron para mí. Los agregué a la configuración del host virtual en lugar de en un archivo .htaccess separado.
Boerre el
6

Si tiene una copia de seguridad de su index.html, asegúrese de que en su archivo index.html tenga esto:

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

Esto puede diferir de un proyecto a otro.

Matt Goo
fuente
2
Agregue esto a su html head: <base href = "/">
Efe Ariaroo
4

Si está utilizando Firebase, todo lo que tiene que hacer es asegurarse de tener una propiedad de reescritura en su archivo firebase.json en la raíz de su aplicación (en la sección de alojamiento).

Por ejemplo:

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

Espero que esto le ahorre a alguien más un montón de frustración y pérdida de tiempo.

Feliz codificación ...

Lecturas adicionales sobre el tema:

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

Firebase CLI: "Configurar como una aplicación de una sola página (reescribir todas las URL en /index.html)"

Joshua Dyck
fuente
Eres una leyenda
Perniferous
4

Encontré la solución para mi SPA con react router (Apache). Simplemente agregue .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>

fuente: https://gist.github.com/alexsasharegan/173878f9d67055bfef63449fa7136042

Sasha Ignashevich
fuente
3

Todavía no estoy usando la representación del lado del servidor, pero me encontré con el mismo problema que el OP, donde Link parecía funcionar bien la mayor parte del tiempo pero fallaba cuando tenía un parámetro. Documentaré mi solución aquí para ver si ayuda a alguien.

Mi jsx principal contiene esto:

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

Esto funciona bien para el primer enlace coincidente, pero cuando: id cambia en <Link> expresiones anidadas en la página de detalles de ese modelo, la url cambia en la barra del navegador pero el contenido de la página no cambió inicialmente para reflejar el modelo vinculado.

El problema era que había usado el props.params.idpara configurar el modelo componentDidMount. El componente solo se monta una vez, por lo que esto significa que el primer modelo es el que se pega en la página y los enlaces posteriores cambian los accesorios pero dejan la página sin cambios.

Establecer el modelo en el estado del componente en ambos componentDidMounty en componentWillReceiveProps(donde se basa en los siguientes accesorios) resuelve el problema y el contenido de la página cambia para reflejar el modelo deseado.

Paul Whipp
fuente
1
Puede ser mejor usar el propsiso del constructor de componentes (que también tiene acceso a ) componentDidMountsi alguna vez quieres probar la representación del lado del servidor. Porque componentDidMountsolo se llama en el navegador. Su propósito es hacer cosas con el DOM, como adjuntar oyentes de eventos, bodyetc., que no puede hacer render.
Stijn de Witt
3

Este tema es un poco antiguo y está resuelto, pero me gustaría sugerirle una solución simple, clara y mejor. Funciona si usas un servidor web.

Cada servidor web tiene la capacidad de redirigir al usuario a una página de error en el caso de http 404. Para resolver este problema, debe redirigir al usuario a la página de índice.

Si utiliza el servidor base Java (tomcat o cualquier servidor de aplicaciones java), la solución podría ser la siguiente:

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>

Ejemplo:

  • OBTENER http://example.com/about
  • El servidor web arroja http 404 porque esta página no existe en el lado del servidor
  • la configuración de la página de error le dice al servidor que envía la página index.jsp al usuario
  • entonces JS hará el resto del trabajo en el lado del cliente porque la url en el lado del cliente todavía es http://example.com/about .

Eso es todo, no más necesidades mágicas :)

zappee
fuente
¡Esto fue asombroso! Estoy usando el servidor Wildfly 10.1 e hice esta actualización en mi archivo web.xml, excepto que tenía la ubicación establecida en '/'. Esto se debió a que en mi código de reacción utilicé el historial del navegador de esta manera:const browserHistory = useRouterHistory(createHistory)({ basename: '/<appname>' });
Chuck L
1
¿Estás seguro de que esto no afecta el SEO? Está dando un estado 404 a las páginas que realmente existen. Es posible que el usuario nunca se dé cuenta de esto, pero los bots prestan mucha atención, de hecho tanto, que no rasparán su página.
subharb
3

Si está utilizando Express o algún otro marco en el back-end, puede agregar la configuración similar a la siguiente y verificar la ruta pública de Webpack en la configuración, debería funcionar bien incluso en la recarga si está utilizando BrowserRouter

expressApp.get('/*', (request, response) => {
    response.sendFile(path.join(__dirname, '../public/index.html'));
});
usuario2849063
fuente
Esta es la solución más simple. tenga en cuenta que esta ruta debe ir después de cualquier otra ruta, ya que es una
trampa para
3

Arreglar el error "no se puede OBTENER / URL" al actualizar o al llamar a la URL directamente.

Configure su webpack.config.js para esperar el enlace dado en rutas como esta.

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

Como estoy usando .Net Core MVC, algo como esto me ayudó:

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

Básicamente en el lado de MVC, todas las rutas que no coinciden se ajustarán a las Home/Indexespecificadas startup.cs. En el interior Index, es posible obtener la URL de solicitud original y pasarla donde sea necesario.

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" });
        });
Elnoor
fuente
Si la API web está separada de la aplicación, ¿cómo manejará esto?
dayanrr91
@ dayanrr91 si su proyecto tiene una API web, de todos modos no necesitará enrutamiento del lado del servidor. Y creo que su router de reacción debería leer la url y hacer el enrutamiento apropiado. Nada debería bloquearlo
Elnoor
su comentario es muy apreciado, pero luego tengo una pregunta, acabo de agregar HashRouter, pero ahora cuando ingreso a cualquier URL manualmente, redirigirá a mi componente de inicio en lugar de redirigir a mi hogar y luego al componente que estaba tratando de entrando, ¿hay alguna solución para eso?
dayanrr91
@ dayanrr91 no tengo idea, no he probado nunca el hashrouter, pero creo que debería tener algo que ver con tu código. La forma en que funciona hashrouter debería ser similar a la de browserrouter. También acabo de probar aquí en este sandbox, funciona bien codesandbox.io/s/25okp1mny , pruebe la url 25okp1mny.codesandbox.io/#/roster/5
Elnoor
2

Si está alojando en IIS; Agregar esto a mi configuración web resolvió mi 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>

Puede hacer una configuración similar para cualquier otro servidor

Barny
fuente
1

En caso de que alguien esté buscando una solución en React JS SPA con Laravel. La respuesta aceptada es la mejor explicación de por qué ocurren tales problemas. Como ya se explicó, debe configurar tanto el lado del cliente como el del servidor. En su plantilla Blade, incluya el archivo incluido js, ​​asegúrese de usarlo URL facadeasí

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

En sus rutas, asegúrese de agregar esto al punto final principal donde está la plantilla de la hoja. Por ejemplo,

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

Lo anterior es el punto final principal para la plantilla de la hoja. Ahora agregue una ruta opcional también,

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

El problema que ocurre es que primero se carga la plantilla de la cuchilla, luego el enrutador de reacción. Entonces, cuando está cargando '/setting-alerts', carga el html y el js. Pero cuando carga '/setting-alerts/about', primero se carga en el lado del servidor. Como en el lado del servidor, no hay nada en esta ubicación, devuelve no encontrado. Cuando tiene ese enrutador opcional, carga la misma página y el enrutador de reacción también se carga, luego el cargador de reacción decide qué componente mostrar. Espero que esto ayude.

Koushik Das
fuente
¿Es posible cargar a un URI exacto al servidor y luego el servidor realiza una redirección a React? Como ejemplo, cuando cargo /user/{userID}, solo necesito devolver una /uservista HTML universal , traer la ID y llamarla usando AJAX o axios. ¿Es posible hacer eso? ¿Son amigables con el SEO?
Ryuujo
1

Para aquellos que usan IIS 10, esto es lo que debe hacer para hacer esto bien. Asegúrese de estar usando browserHistory con esto. En cuanto a la referencia, daré el código para el enrutamiento, pero esto no es lo que importa, lo que importa es el siguiente paso después del código del componente a continuación:

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

Dado que el problema es que IIS recibe una solicitud de los navegadores del cliente, interpretará la URL como si estuviera pidiendo una página, luego devolverá una página 404 ya que no hay una página disponible. Haz lo siguiente:

  1. Abrir IIS
  2. Expanda el servidor y luego abra la carpeta Sitios
  3. Haga clic en el sitio web / aplicación
  4. Ir a las páginas de error
  5. Abra el elemento de estado de error 404 en la lista
  6. En lugar de la opción "Insertar contenido del archivo estático en la respuesta de error", cámbielo a "Ejecutar una URL en este sitio" y agregue el valor de barra diagonal "/" a la URL.

Y ahora funcionará bien.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Espero que ayude. :-)

Martin Lloyd Jose
fuente
1

Estoy usando WebPack, tuve el mismo problema Solución => En su archivo 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();
  });

¿Por qué mi aplicación no se procesa después de actualizar?

Vishal Singh
fuente
¡Esto lo hizo por mí! Inicialmente tenía res.sendFile (path.JOIN (publicPath, "index.html")); Cambié "unirse" a "resuelto" como en el ejemplo anterior a: res.sendFile (path.resolve ("./ dist", "index.html")); También estaba jugando con __dirname pero realmente no podía entenderlo o hacerlo funcionar, así que copié manualmente en "./dist" porque aquí es donde se sirve mi index.html. También lo dije así antes: app.use (express.static ("./ dist"));
Logixplayer
1

Usar HashRouterfuncionó para mí con Redux también, simplemente reemplace:

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

a:

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();
Alireza
fuente
0

Tuve el mismo problema y esta solución funcionó para nosotros.

Antecedentes:

Estamos alojando múltiples aplicaciones en el mismo servidor. Cuándo actualizaríamos el servidor no entendería dónde buscar nuestro índice en la carpeta dist para esa aplicación en particular. El enlace anterior lo llevará a lo que funcionó para nosotros ... Espero que esto ayude, ya que hemos dedicado muchas horas a encontrar una solución para nuestras necesidades.

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

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

my 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!');
});
J. Parrish
fuente
0

Me gusta esta forma de manejarlo. Intente agregar: yourSPAPageRoute / * en el lado del servidor para deshacerse de este problema.

Seguí este enfoque porque incluso la API de historial HTML5 nativa no admite la redirección correcta en la actualización de la página (que yo sepa).

Nota: La respuesta seleccionada ya ha abordado esto, pero estoy tratando de ser más específico.

Ruta Express

Prueba: API de historial Probado y solo quería compartir esto.

Espero eso ayude.

Rehan
fuente