Implementar una aplicación React en Microsoft Azure es simple. Excepto que… no lo es. El diablo está en los detalles. Si está buscando implementar una aplicación de creación-reacción, o un marco de JavaScript de front-end de estilo similar que requiere pushState
enrutamiento basado en Microsoft Azure, creo que este artículo le será útil. Vamos a tratar de evitar los dolores de cabeza de la conciliación de enrutamiento del lado del cliente y del servidor.
Primero, una historia rápida.
En 2016, cuando Donovan Brown, gerente sénior del programa DevOps en Microsoft, pronunció un discurso de “pero funciona en mi máquina” en Microsoft Connect ese año, todavía estaba en mis etapas preliminares como desarrollador web. Su charla fue sobre microservicios y contenedores.
[…] Atrás quedaron los días en que su gerente llega corriendo a su oficina y está frenética y ha encontrado un error. Y no importa cuánto lo intente, no puedo reproducirlo y funciona perfectamente en mi máquina. Ella dice: bien Donovan, entonces enviaremos tu máquina porque ese es el único lugar donde funciona. Pero me gusta mi máquina, así que no voy a dejar que la envíe…
Tuve un tipo de desafío similar, pero tenía que ver con el enrutamiento. Estaba trabajando en un sitio web con un front-end React y un back-end ASP.NET Core, alojados como dos proyectos separados que se implementaron en Microsoft Azure. Esto significaba que podíamos implementar ambas aplicaciones por separado y disfrutar de los beneficios que conlleva la separación de preocupaciones. También sabemos a quién git blame
si y cuando algo sale mal. Pero también tenía desventajas, ya que la conciliación de enrutamiento de front-end vs. back-end era una de esas desventajas.
Un día publiqué un código nuevo en nuestros servidores de prueba. Recibí un mensaje poco después de decirme que el sitio web estaba fallando en la actualización de la página. Estaba arrojando un error 404. Al principio, no pensé que era mi responsabilidad corregir el error. Tuvo que ser algún problema de configuración del servidor. Resulta que tenía razón y estaba equivocado.
Tenía razón al saber que era un problema de configuración del servidor (aunque en ese momento no sabía que tenía que ver con el enrutamiento). Me equivoqué al negarle mi responsabilidad. Fue solo después de que comencé a buscar en la web que encontré un caso de uso para implementar una aplicación de creación y reacción en Azure en la pestaña Implementación en la página de documentación oficial.
Construyendo React para producción
Al crear una aplicación React para producción (asumiendo que estamos usando create-react-app), vale la pena tener en cuenta las carpetas que se generan. Corriendo npm run build
generará una carpeta de compilación donde vive una versión estática optimizada de la aplicación. Para obtener la aplicación en un servidor en vivo, todo lo que necesitamos hacer es alimentar al servidor con el contenido de la carpeta de compilación. Si estuviéramos trabajando en servidor local, no hay un servidor en vivo involucrado, por lo que no siempre es equivalente a tener la aplicación en un servidor en vivo.
Generalmente, la carpeta de compilación tendrá esta estructura:
→ build
→ static
→ css
→ css files
→ js
→ js files
→ media
→ media files
→ index.html
→ other files...
Enrutamiento del lado del cliente con React Router
React Router usa HTML5 pushState
API de historial internamente. Qué pushState
hace es bastante interesante. Por ejemplo, navegar (o usar Link en el enrutador de reacción) desde la página https://css-tricks.com
a la página https://css-tricks.com/archives/
hará que se muestre la barra de URL https://css-tricks.com/archives/
pero no hará que el navegador cargue la página /archives
o incluso comprobar que existe. Combine esto con el modelo basado en componentes de React, se convierte en algo para cambiar las rutas mientras se muestran diferentes páginas basadas en esas rutas, sin el ojo que todo lo ve del servidor tratando de servir una página en su propio directorio. Entonces, ¿qué sucede cuando introducimos servidores empujando el código a un servidor en vivo? Los documentos lo dicen mejor:
Si usa enrutadores que usan la API de historial HTML5 pushState bajo el capó (por ejemplo, React Router con browserHistory), muchos servidores de archivos estáticos fallarán. Por ejemplo, si usó React Router con una ruta para /todos/42, el servidor de desarrollo responderá a localhost:3000/todos/42 correctamente, pero un Express que sirve una compilación de producción como la anterior no lo hará. Esto se debe a que cuando se carga una página nueva para /todos/42, el servidor busca el archivo build/todos/42 y no lo encuentra. El servidor debe configurarse para responder a una solicitud a /todos/42 sirviendo index.html.
Diferentes servidores requieren una configuración diferente. Express, por ejemplo, requiere esto:
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
…como se documenta en los documentos create-react-app. Sin embargo, tenga en cuenta que esto supone que estamos hospedando create-react-app en la raíz del servidor, que está haciendo uso de una ruta comodín (*
) que captura todas las rutas y responde a todas las solicitudes de ruta sirviendo el index.html
archivo en la carpeta de compilación que se encuentra en la raíz de la aplicación del servidor. Además, esto está estrechamente relacionado con el back-end. Si ese es el caso, lo más probable es que tengamos este tipo de estructura de carpetas (suponiendo que el back-end esté en NodeJS):
→ Server
→ Client (this is where your react code goes)
→ build (this is the build folder, after you npm run build)
→ src
→ node_modules
→ package.json
→ other front-end files and folders
→ Other back-end files and folders
Dado que mi front-end (create-react-app) y back-end (ASP.NET) eran dos proyectos diferentes, servir archivos estáticos navegando por el directorio era una especie de imposibilidad.
De hecho, dado que estamos implementando una aplicación estática, no necesitamos el back-end. Como dijo Burke Holland: “Estático” significa que no estamos implementando ningún código de servidor; solo los archivos front-end.
Sigo mencionando ASP.NET aquí porque durante el curso de mi investigación, pensé que configurar Azure requería un archivo de configuración en un wwwroot
carpeta y la estructura de carpetas de ASP.NET normalmente tiene una wwwroot
carpeta. ¿Recuerda que el back-end de la aplicación estaba en ASP.NET? Pero eso es todo. El wwwroot
La carpeta parecía estar oculta en algún lugar de Azure. Y no puedo mostrártelo sin desplegar un create-react-app
. Así que vamos a hacer eso.
Primeros pasos con App Services en Microsoft Azure
Para comenzar, si aún no tiene una cuenta de Azure, obtenga una prueba gratuita y luego diríjase al portal de Azure.
- Vaya a Todos los servicios → Web → Servicios de aplicaciones
Navegación en Azure Portal desde Todos los servicios, a Web, a Servicios de aplicaciones
- Queremos agregar una nueva aplicación, darle un nombre, suscripción (debe ser gratuita si está en una prueba gratuita o si ya tiene una), grupo de recursos (cree uno o use uno existente), luego haga clic en Crear botón hacia abajo en la parte inferior del panel.
Creación de un nuevo servicio de aplicaciones en Azure Portal. - Deberíamos recibir una notificación de que el recurso ha sido creado. Pero no aparecerá de inmediato, así que presione “Actualizar”. Tengo otros recursos, pero AzureReactDemo2 es lo que estoy usando aquí. Hará clic en el nombre de su aplicación recién creada, que en mi caso es AzureReactDemo2.
Visualización de todos los servicios de aplicaciones en Azure Portal. - La hoja le muestra información sobre su aplicación, la navegación a la izquierda tiene todo lo que necesita para administrar su aplicación (resumen, registro de actividad, centro de implementación…).
por ejemplo, el Despliegue Centrar es donde se gestiona la implementación de la aplicación, Tragamonedas es donde se gestionan cosas como la puesta en escena, la producción y las pruebas. Configuración es donde se gestionan cosas como variables ambientales, versiones de nodos y, uno importante, Kudu.
La pantalla de descripción general muestra una vista general del estado de la aplicación, URL… Haga clic en la URL para ver el sitio en vivo.
Muestra las diversas partes de un servicio de aplicaciones en la CLI de Azure.
¡La aplicación está funcionando!
Mostrando la página en vivo predeterminada de un servicio de aplicaciones.
Lo que hemos hecho es crear un nuevo servicio de aplicaciones, pero todavía no tenemos ninguno de nuestros códigos en Azure. Como se dijo anteriormente, todo lo que tenemos que hacer es alimentar a Azure con el contenido de la carpeta de compilación generada al compilar React para producción, pero aún no tenemos una. Así que vayamos a lo local y obtengamos una aplicación React.
Yendo local
Necesitamos crear una nueva aplicación React e instalar react-router como una dependencia.
npx create-react-app azure-react-demo
cd azure-react-demo
También queremos instalar react-router (react-router-dom
, Realmente)
npm i react-router-dom
En igualdad de condiciones, iniciar la aplicación con npm start
, deberíamos obtener la página predeterminada.
Mostrando la página predeterminada generada por React.
Debido a que se tratará de probar rutas, necesitaba hacer algunas páginas. Modifiqué mi versión local y la subí a GitHub. Confío en el hecho de que puede orientarse en reaccionar y enrutador de reacción. Descargue una demostración.
Mi carpeta se ve así:
Mostrando las carpetas y archivos en la aplicación create-react-app modificada.
Los archivos modificados tienen el siguiente código:
// App.js
import React, { Component } from "react";
import "./App.css";
import Home from "./pages/Home";
import Page1 from "./pages/Page1";
import Page2 from "./pages/Page2";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path="https://css-tricks.com/" component={Home} />
<Route path="/page1" component={Page1} />
<Route path="/page2" component={Page2} />
</Switch>
</Router>
);
}
}
export default App;
// Page1.js
import React from "react";
import { Link } from "react-router-dom";
const Page1 = () => {
return (
<div className="page page1">
<div className="flagTop" />
<div className="flagCenter">
<h1 className="country">Argentina (PAGE 1)</h1>
<div className="otherLinks">
<Link to="/page2">Nigeria</Link>
<Link to="https://css-tricks.com/">Home</Link>
</div>
</div>
<div className="flagBottom" />
</div>
);
};
export default Page1;
// Page2.js
import React from "react";
import { Link } from "react-router-dom";
const Page2 = () => {
return (
<div className="page page2">
<div className="flagTop" />
<div className="flagCenter">
<h1 className="country">Nigeria (PAGE 2)</h1>
<div className="otherLinks">
<Link to="/page1">Argentina</Link>
<Link to="https://css-tricks.com/">Home</Link>
</div>
</div>
<div className="flagBottom" />
</div>
);
};
export default Page2;
/* App.css */
html {
box-sizing: border-box;
}
body {
margin: 0;
}
.page {
display: grid;
grid-template-rows: repeat(3, 1fr);
height: 100vh;
}
.page1 .flagTop,
.page1 .flagBottom {
background-color: blue;
}
.page2 .flagTop,
.page2 .flagBottom {
background-color: green;
}
.flagCenter {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
text-align: center;
}
.page a {
border: 2px solid currentColor;
font-weight: bold;
margin: 0 30px;
padding: 5px;
text-decoration: none;
text-transform: uppercase;
}
.flags {
display: flex;
width: 100%;
}
.flags > .page {
flex: 1;
}
Ejecutar la aplicación funciona localmente, por lo que las rutas se entregan cuando links
se hace clic e incluso cuando se actualiza la página.
Implementar la aplicación en Azure
Ahora, ¡hagámoslo en Azure! Hay algunos pasos para que esto suceda.
Paso 1: diríjase al Centro de implementación
En Azure, debemos ir al Centro de implementación. Hay bastantes opciones, cada una con sus pros y sus contras. Usaremos Git local (lo que significa su aplicación Git local directamente a Azure) para el control de código fuente, Kudu para el proveedor de compilación.
Recuerde hacer clic en continuar o finalizar cuando seleccione una opción, de lo contrario, el portal seguirá mirándolo.
Mostrar el Centro de implementación en Azure Portal y elegir un control de código fuente como primer paso para implementar un nuevo Servicio de aplicaciones.
Mostrando la sección Proveedor de compilación en el Centro de implementación en Azure Portal.
Después del tercer paso, Azure genera un repositorio git local para usted. Y le brinda un enlace remoto para apuntar su aplicación de reacción.
Una cosa a tener en cuenta en este punto. Cuando presione, Azure le pedirá sus credenciales de GitHub. Está debajo de la pestaña de implementación. Hay dos: Aplicación y Usuario. La credencial de la aplicación será específica para una aplicación. El usuario será general para todas las aplicaciones a las que usted como usuario tiene acceso de lectura/escritura. Puede prescindir de las credenciales de usuario y usar las credenciales de la aplicación, pero descubrí que, después de un tiempo, Azure deja de solicitar las credenciales y solo me dice que la autenticación falló automáticamente. Configuré unas Credenciales de usuario personalizadas. De cualquier manera, deberías superar eso.
Mostrar las Credenciales de implementación para App Service en Azure Portal.
En la aplicación React, después de la modificación, necesitamos compilar para producción. Esto es importante porque lo que queremos cargar es el contenido de la carpeta de compilación.
Necesitamos decirle a Kudu qué motor de nodo usaremos, o de lo contrario, lo más probable es que la compilación falle.
debido al hecho informado de que react-scripts
requiere una versión de nodo superior a la predeterminada establecida en Azure. Hay otras formas de hacerlo, pero la más simple es agregar un motor de nodos en package.json
. Estoy usando la versión 10.0 aquí. Desafortunadamente, no podemos simplemente agregar lo que nos gusta, ya que Azure tiene versiones de Node que admite y el resto no lo es. Verifique eso con la CLI con el comando: az webapp list-runtimes
Agregue la versión de nodo preferida a la package.json
archivo, como en:
"engines": {
"node": "10.0"
}
Visualización de una lista de tiempos de ejecución de Azure en la CLI de Azure.
Paso 2: Cree la aplicación
Para construir la aplicación React, ejecutemos npm build
en la Terminal.
Paso 3: inicializa el repositorio de Git
Navegue a la carpeta de compilación e inicialice un repositorio de Git allí. La URL para clonar el repositorio se encuentra en la página de información general. Dependiendo de las credenciales que esté utilizando (aplicación o usuario), será ligeramente diferente.
Mostrando la descripción general de App Service en Azure y la URL del clon de Git.
git init
git add .
git commit -m "Initial Commit"
git remote add azure <git clone url>
git push azure master
Ahora, visite la aplicación en vivo usando la URL en la página de descripción general. Como puede ver, la aplicación falla en /page2
actualizar. Mirando la pestaña de la red, se lanza un 404 porque la página intentó ser recuperada del servidor; con el enrutamiento del lado del cliente, como ya hemos configurado, la página ni siquiera debería ser recuperada por el servidor.
Mostrando la solicitud de página fallida y la pestaña de red para verificar.
Configuración de Azure para reconciliar el enrutamiento del lado del servidor y del cliente
En la carpeta pública, agreguemos un web.config
Archivo XML con el siguiente contenido:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="React Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
</conditions>
<action type="Rewrite" url="https://css-tricks.com/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
He decidido intencionalmente no formatear el fragmento de código porque XML es estricto al respecto. Si pierde el formato, el archivo no tiene ningún efecto. Puede descargar un formateador XML para su editor de texto. Para VSCode, ese sería el complemento Herramientas XML.
Mostrando un formateador XML y un archivo con formato XML en VSCode.
La aplicación se puede compilar nuevamente en este punto, aunque perderemos la información de Git en la carpeta de compilación, ya que la nueva compilación anula la compilación anterior. Eso significa que tendría que agregarse nuevamente y luego empujarse.
¡Ahora la aplicación funciona como se muestra a continuación! Uf.
no queremos tener que npm run build
cada vez, ahí es donde entra en juego la implementación continua. Consulte el siguiente enlace para obtener las referencias adecuadas.
Conclusión
Hay mucho en Azure, ya que puede hacer mucho por usted. Eso es bueno porque hay ocasiones en las que lo necesita para hacer algo que parece súper específico, como hemos visto aquí con la reconciliación de enrutamiento del lado del cliente y del servidor, y ya lo respalda.
Dicho esto, lo dejaré con un par de recursos relacionados a los que puede recurrir mientras busca implementar una aplicación React en Azure.
- Implementación personalizada de NodeJs en Azure Web App por Hao Luo: obtenga más información sobre la implementación de Kudu y NodeJS.
- Implementación de una aplicación React como un sitio estático en Azure por Burke Holland: aún más opciones para implementar la aplicación create-react en Microsoft Azure.