¿Cómo configurar correctamente Apache en una aplicación SPA React que reside en un subdirectorio que usa URL anidadas creadas a partir de React Router?
Tengo mi sitio web alojado en un subdirectorio, por ejemplo, http: // mywebsite / admin, donde / admin es el subdirectorio
Tengo mi aplicación de reacción (creación de producción de la aplicación de reacción) cargada en el subdirectorio / admin. También estoy usando react-router v4 BrowserRouter para realizar un enrutamiento simple del lado del cliente. A continuación se muestran algunos fragmentos relevantes en mi aplicación de reacción:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import { BrowserRouter } from 'react-router-dom';
import './css/custom.css';
ReactDOM.render(<BrowserRouter basename="/admin"><App /></BrowserRouter>, document.getElementById("root"));
dentro de la función de renderización App.jsx
<Route
path='/students'
render={(props) =>
<Students
{...props}
/>
}
/>
<Route
path='/teachers'
render={(props) =>
<Teachers
{...props}
/>
}
/>
Todo está bien y funcionando al navegar por el sitio, incluidas las URL anidadas que son principalmente, como ha visto en los fragmentos, /admin/students
& /admin/teachers
.
El problema principal: cuando estoy en la /admin/teachers
ruta y actualizo la página , la página no carga nada. Refrescante funciona solo cuando estoy en /admin
ruta. Pero no cuando es /admin/teachers
ni /admin/students
.
Esta no es la primera vez que construyo un SPA usando react-router v4 alojado en el servidor Apache. He reutilizado el .htaccess que tenía en mi proyecto anterior. Este .htaccess también reside en el /admin
subdirectorio. A continuación se muestra .htaccess
:
.htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.html?path=$1 [NC,L,QSA]
Esto enruta todas las solicitudes al index.html creado por la compilación de producción create-react-app. Sin embargo, según mis investigaciones, este tipo de configuración solo funciona en un proyecto que no está en un subdirectorio y no usa URL anidadas.
Tenga en cuenta que .htaccess
ahora tengo 2 . Uno en la raíz y otro en el /admin
subdirectorio. Ambos contienen los mismos .htaccess
códigos . La razón de esto es que tengo otro SPA ejecutándose en el directorio raíz.
¿Qué me estoy perdiendo?
¿Cómo configurar correctamente Apache en una aplicación SPA React que reside en un subdirectorio que usa URL anidadas creadas a partir de React Router?
Respuestas
Abordar principalmente la respuesta del OP , más algunas otras notas ...
Aquí es donde cometí un error:
Tenga en cuenta que ahora tengo 2 .htaccess. Uno en root y otro en el subdirectorio / admin. Ambos contienen los mismos códigos .htaccess. La razón de esto es que tengo otro SPA ejecutándose en el directorio raíz.
Esto no fue necesariamente un "error", solo debe asegurarse de que está utilizando las directivas correctas. De hecho, .htaccess
podría ser preferible mantener los dos archivos separados si se pretende que sean SPA completamente separados.
Para tener dos .htaccess
archivos separados, idénticos, los puntos importantes son:
- Haga que la cadena de sustitución sea relativa , es decir. sin prefijo de barra.
- No incluya una
RewriteBase
directiva. (Lo que anularía el prefijo de directorio).
Cuando la cadena de RewriteRule
sustitución es una ruta relativa , el prefijo del directorio (es decir, la ubicación del .htaccess
archivo) se vuelve a agregar al final del proceso de reescritura.
Entonces, ambos /.htaccess
y /admin/.htaccess
contendrían directivas idénticas:
RewriteEngine on
RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.+) index.html?path=$1 [L,QSA]
SIN EMBARGO, una pequeña diferencia con esta solución y la solución del OP es que la admin/
parte de la ruta URL no se pasa al path
parámetro URL. Puede codificar esto si es necesario. p.ej. index.html?path=admin/$1
. O tenga en cuenta esto en su guión.
La primera RewriteRule
es solo una optimización menor que evita más verificaciones del sistema de archivos después de que la solicitud ya se haya reescrito index.html
.
Tenga en cuenta que cambié (.*)
(0 o más) a (.+)
(1 o más). Esto evita la verificación de directorio adicional al solicitar el directorio raíz. es decir. example.com/
y example.com/admin/
. No está reescribiendo estas solicitudes, sino que depende de que DirectoryIndex index.html
se configure adecuadamente; esta es la configuración predeterminada en la configuración del servidor.
Dado que la cadena de sustitución ( index.php?page=$1
) es relativa, cuando esté en la raíz del documento se reescribirá efectivamente /index.php
y cuando /admin/.htaccess
esté en ella se reescribirá efectivamente /admin/index.php
.
Tenga en cuenta que las directivas mod_rewrite /admin/.htaccess
anulan completamente las directivas mod_rewrite en el .htaccess
archivo principal , ya que las directivas mod_rewrite no se heredan de forma predeterminada (a diferencia de otros módulos).
Alternativamente, para evitar la repetición, mientras mantiene dos .htaccess
archivos separados , puede habilitar la herencia mod_rewrite en el /admin/.htaccess
archivo. Por ejemplo...
En el /.htaccess
archivo raíz :
RewriteEngine on
RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.+) index.html?path=$1 [L,QSA]
Entonces, en su lugar /admin/.htaccess
tendrías lo siguiente:
RewriteEngine On
RewriteOptions Inherit
Esto hereda las directivas mod_rewrite padre como si se hubieran copiado literalmente en el lugar del /admin/.htaccess
archivo.
Sin embargo, esto agrega una capa adicional de complejidad. Además de que el SPA /admin
ahora depende del .htaccess
archivo raíz , ahora debe tener cuidado de no agregar ninguna directiva de ruptura al .htaccess
archivo raíz .
Aparte:
RewriteCond %{REQUEST_URI} admin RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*) admin/index.html?path=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*) index.html?path=$1 [QSA,L]
La primera condición que busca "admin" en la ruta de la URL (por cierto, esto busca "admin" en cualquier parte de la ruta de la URL, lo cual no es estrictamente correcto) no es necesaria; la verificación se puede realizar de manera más óptima en el RewriteRule
directiva en sí. Por ejemplo:
RewriteRule ^(admin/.*) admin/index.html?path=$1 [QSA,L]
Estas dos reglas se pueden combinar en una si se desea, posiblemente a expensas de cierta claridad. Por ejemplo:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(admin/)?.* $1index.html?path=$0 [QSA,L]
La $1
referencia inversa entonces contiene el prefijo de ruta opcional, ya sea admin/
o nada . Y $0
contiene el partido completo.
Aquí es donde cometí un error:
Tenga en cuenta que ahora tengo 2 .htaccess. Uno en root y otro en el subdirectorio / admin. Ambos contienen los mismos códigos .htaccess. La razón de esto es que tengo otro SPA ejecutándose en el directorio raíz.
Simplemente ajustando mi raíz .htaccess
y eliminando el .htaccess
que estaba dentro del subdirectorio / admin solucionó el problema principal descrito en mi pregunta.
Raíz .htaccess :
RewriteCond %{REQUEST_URI} admin
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) admin/index.html?path=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*) index.html?path=$1 [QSA,L]
De hecho, no conozco la respuesta a su problema, pero puedo sugerirle que juegue con la Location
directiva; algo como
<Location "/admin">
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /admin/index.html?path=$1 [NC,L,QSA]
</Location>
Espero que esto ayude.