Continuamos y terminamos con la serie de tutoriales dedicados a ver cómo una aplicación híbrida de escritorio viendo cómo combinar nuestro Backend de .NET, esta vez con un frontend en Astro.
Ya hemos visto en los cómo integrar frameworks de desarrollo populares como React o Vue.js. Hoy nos enfocaremos en Astro, un framework moderno que permite construir cómodamente aplicaciones web rápidas y muy optimizadas.
Una de las ventajas de Astro será la posibilidad de su hidratación de islas, lo que nos permite usar componentes de otros frameworks (incluidos React y Vue).
Además, su sistema de routing estático nos evitará tener que usar un router de un framework, entre otras muchas ventajas. Así que vamos a por ello 👇.
Si te perdiste los artículos anteriores, aquí los tienes: Cómo crear una aplicación híbrida WPF + ASP.NET Core + React Cómo crear una aplicación híbrida WPF + ASP.NET Core + Vue.js
Crear el proyecto Astro
Para comenzar, necesitamos crear nuestra aplicación web con Astro. Abrimos una terminal en la raíz de la solución y ejecutamos los siguientes comandos:
npm create astro@latest AstroClient
cd AstroClient
npm install
Esto nos generará un proyecto básico de Astro, un framework que se destaca por su rendimiento y flexibilidad.
Configurar Astro para trabajar con el backend
Al igual que ocurría con los ejemplos de React y Vue, necesitamos configurar nuestra aplicación Astro se comunique correctamente con el backend de ASP.NET Core.
Primero, creamos un archivo .env
en la carpeta AstroClient
con la siguiente línea,
PUBLIC_API_URL=http://localhost:5000
Y un fichero .env.production
, que nos permitirá que la aplicación funcione en producción aunque el puerto de la app ASP sea dinámico.
PUBLIC_API_URL=
Daros cuenta de que esta vez la variable se llama PUBLIC_API_URL
, y no VITE_API_URL
como en los tutoriales anteriores
Por otro lado, modificamos el archivo vite.config.js
para configurar el proxy:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
"/api": "http://localhost:5000", // Redirige las peticiones a la API de C#
},
},
});
Con esta configuración, todas las solicitudes que comiencen con /api
se redirigirán automáticamente a nuestro servidor ASP.NET Core, evitando problemas de CORS y simplificando el desarrollo.
Añadir integración con Vue (opcional)
Una de las ventajas de Astro es que podemos usar su hidratación de islas para integrar componentes de otros frameworks, como Vue (para nuestra demo, vamos a añadir una integración con Vue).
npm install @astrojs/vue
Luego, modificamos el archivo astro.config.mjs
para incluir la integración de Vue:
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
export default defineConfig({
integrations: [vue()],
});
Crear el layout
También añadimos un layout básico que servirá como estructura común para todas nuestras páginas. Para ello creamos un archivo Layout.astro
en la carpeta src/layouts
:
---
import Navbar from "../components/Navbar.vue";
---
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Astro + Vue App</title>
</head>
<body>
<Navbar />
<main>
<slot />
</main>
</body>
</html>
Crear las páginas
Ahora vamos a crear nuestra páginas. Astro usa un sistema de rutas basado en ficheros. Es decir, que cada fichero que tengamos en la ruta src/pages
se acabará convirtiendo en un endpoint de nuestra aplicación.
Vamos a ver las páginas que tenemos
La página de inicio es sencilla y solo muestra un mensaje de bienvenida:
---
import Layout from '../layouts/Layout.astro';
---
<Layout>
<h1>Home</h1>
<p>Esta es la página de Home.</p>
</Layout>
Esta página es estática y sirve como ejemplo de cómo crear páginas simples:
---
import Layout from '../layouts/Layout.astro';
---
<Layout>
<h1>Acerca de</h1>
<p>Esta es la página de "Acerca de".</p>
</Layout>
En esta página, demostramos cómo recuperar parámetros de la URL y pasarlos a un componente de Vue:
---
import Layout from '../layouts/Layout.astro';
import QueryParams from '../components/QueryParams.vue';
---
<Layout>
<QueryParams client:load/>
</Layout>
Esta página muestra cómo consumir datos de la API de ASP.NET Core utilizando un componente de Vue:
---
import Layout from '../layouts/Layout.astro';
import Fetch from '../components/Fetch.vue';
---
<Layout>
<h1>Bienvenido a la App</h1>
<Fetch client:load/>
</Layout>
Esta página integra SignalR para recibir actualizaciones en tiempo real desde el servidor:
---
import Layout from '../layouts/Layout.astro';
import ClockComponent from '../components/ClockComponent.vue';
---
<Layout>
<ClockComponent client:load/>
</Layout>
Crear los componentes
Ya tenemos las páginas creadas. Ahora, para darle dinamismo a nuestra web podemos usar un framework como React o Vue, y la funcionalidad de islas de Astro.
Ya había dicho que para este ejemplo ibamos a integrar Vue.js. Vamos a ver los componentes de Vue que necesitaríamos para nuestra aplicación.
El menú de navegación (este perfectamente podríamos haberlo hecho en Astro).
<template>
<nav>
<ul>
<li><a href="/">Inicio</a></li>
<li><a href="/about">Acerca de</a></li>
<li><a href="/query?q=123">Query</a></li>
<li><a href="/fetch">Fetch</a></li>
<li><a href="/signalr">SignalR</a></li>
</ul>
</nav>
</template>
Este componente muestra cómo recuperar y mostrar parámetros de la URL. En el caso de Astro, el routing lo estamos haciendo a través de Astro, que convierte los pages en Rutas estáticas.
Por tanto, en este ejemplo vamos a ver como podríamos obtener los parámetros que se pasarían entre páginas, para funcionar como una SPA.
<script setup>
import { ref, onMounted } from 'vue';
const params = ref({});
const data = ref(null);
onMounted(async () => {
const urlSearchParams = new URLSearchParams(window.location.search);
params.value = Object.fromEntries(urlSearchParams.entries());
if (params.value.id) {
const response = await fetch(`${import.meta.env.PUBLIC_API_URL}/api/data?id=${params.value.id}`);
}
});
</script>
<template>
<div>
<h2>Query Parameters</h2>
<pre>{{ params }}</pre>
</div>
</template>
Este componente consume datos de la API de ASP.NET Core:
<script setup>
import { ref, onMounted } from 'vue';
const data = ref(null);
const forecasts = ref([]);
const files = ref([]);
onMounted(() => {
fetch(`${import.meta.env.PUBLIC_API_URL}/api/demo`)
.then(res => res.json())
.then(result => data.value = result);
populateWeatherData();
getFiles();
});
async function populateWeatherData() {
const response = await fetch(`${import.meta.env.PUBLIC_API_URL}/weatherforecast`);
if (response.ok) {
forecasts.value = await response.json();
}
}
async function getFiles() {
const response = await fetch(`${import.meta.env.PUBLIC_API_URL}/files`);
if (response.ok) {
files.value = await response.json();
}
}
</script>
<template>
<div>
<h1>{{ data?.message || 'Cargando...' }}</h1>
<ul v-if="forecasts.length > 0 && files.length > 0">
<li v-for="file in files" :key="file">{{ file }}</li>
</ul>
<p v-else>
<em>Cargando... Por favor, espera a que el backend de ASP.NET se inicie.</em>
</p>
</div>
</template>
Finalmente, es componente muestra la hora actual utilizando SignalR, para ilustrar cómo funciona la comunicación en tiempo real entre Backend y Frontend.
<script setup>
import { ref, onMounted } from 'vue';
import { HubConnectionBuilder } from '@microsoft/signalr';
const time = ref('');
onMounted(() => {
const connection = new HubConnectionBuilder()
.withUrl(`${import.meta.env.PUBLIC_API_URL}/clockHub`)
.build();
connection.on('ReceiveTime', (currentTime) => {
time.value = currentTime;
});
connection.start()
.then(() => console.log('Conexión a SignalR establecida.'))
.catch(err => console.error('Error al conectar con SignalR: ', err));
});
</script>
<template>
<div>
<h1>Hora Actual</h1>
<p>{{ time || 'Sin conexión' }}</p>
</div>
</template>
Una de las partes interesantes de la integración con Astro y las islas de componentes es que vais a tener que decidir que parte hacéis como componentes de Astro, y qué parte como componente de otro Framework.
En esta demo he usado Astro prácticamente sólo para las paginas, y todo el peso está en los componentes de Vue.js. Pero, en general, lo normal es que únicamente usarais framework para componentes dinámicos, y dejar todo el resto a Astro.
Añadir estilos básicos
Finalmente, igual que hcimos en los dos ejemplos de React y Vue, vamos a añadir un poco de CSS para darle un aspecto más profesional a nuestra aplicación:
<style is:global>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f9;
color: #333;
}
nav {
background-color: #1f1d21;
padding: 1rem;
display: flex;
gap: 1rem;
}
nav ul {
list-style: none;
display: flex;
gap: 1rem;
}
nav a {
color: white;
text-decoration: none;
font-weight: bold;
}
nav a:hover {
text-decoration: underline;
}
.container {
padding: 2rem;
}
h1 {
color: #1f1d21;
}
</style>
Ejecutar la aplicación
Una vez que todo está configurado, puedes ejecutar la aplicación y ver nuestra aplicación híbrida funcionando
Descarga el código
Todo el código de esta entrada está disponible para su descarga en Github.
¡Y eso es todo! Con este tutorial, has aprendido a integrar Astro en una aplicación híbrida WPF + ASP.NET Core. Espero que te haya sido útil y que disfrutes trabajando con estas tecnologías. ¡Hasta la próxima! 🚀