Middleware
Un Middleware te permite interceptar las solicitudes y respuestas e inyectar comportamientos dinámicamente cada vez que una página o endpoint está a punto de ser renderizado.
Esto también te permite establecer y compartir información específica de la solicitud en todos los endpoints y páginas mediante la mutación de un objeto locals
que está disponible en todos los componentes de Astro y endpoints de API.
El middleware está disponible tanto en proyectos de Astro SSG como en proyectos de Astro SSR.
Uso básico
Sección titulada Uso básico-
Crea
src/middleware.js|ts
(Alternativamente, puedes crearsrc/middleware/index.js|ts
.) -
Dentro de este archivo, exporta una función
onRequest()
. Esta no debe ser una exportación por defecto.src/middleware.js export function onRequest ({ locals, request }, next) {// interceptar los datos de respuesta de una solicitud.// opcionalmente, puedes transformar la respuesta modificando `locals`.locals.title = "Nuevo título";// devuelve una respuesta o el resultado de llamar a `next()`.return next();}; -
Dentro de cualquier archivo
.astro
, puedes acceder a los datos de respuesta utilizandoAstro.locals
.
---const data = Astro.locals;---<h1>{data.title}</h1><p>Este {data.property} proviene de un middleware.</p>
Tipos de Middleware
Sección titulada Tipos de MiddlewarePuedes importar y usar la función de utilidad defineMiddleware()
para aprovechar la seguridad de tipos:
import { defineMiddleware } from "astro:middleware";
// `context` y `next` son tipados automáticamente.export const onRequest = defineMiddleware((context, next) => {
});
En cambio, si estás utilizando JsDoc para aprovechar la seguridad de tipos, puedes usar MiddlewareRequestHandler
:
// src/middleware.js/** * @type {import("astro").MiddlewareResponseHandler} */// `context` y `next` son tipados automáticamente.export const onRequest = (context, next) => {
};
Para tipar la información dentro de Astro.locals
, que proporciona autocompletado dentro de los archivos .astro
y el código de middleware, declara un espacio de nombres global en el archivo env.d.ts
:
/// <reference types="astro/client" />declare namespace App { interface Locals { user: { name: string }, welcomeTitle: () => string, orders: Map<string, object> }}
Luego, dentro del archivo de middleware, podemos aprovechar el autocompletado y la seguridad de tipos.
Puedes almacenar cualquier tipo de datos dentro de Astro.locals
: cadenas de texto, números e incluso tipos de datos complejos como funciones y maps.
export function onRequest ({ locals, request }, next) { // interceptar los datos de respuesta de una solicitud // opcionalmente, transforma la respuesta modificando `locals`. locals.user.name = "John Wick"; locals.welcomeTitle = () => { return "Bienvenido de nuevo " + locals.user.name; };
// devuelve una respuesta o el resultado de llamar a `next()`. return next();};
Luego puedes utilizar esta información dentro de cualquier archivo .astro
.
---const title = Astro.locals.welcomeTitle();const orders = Array.from(Astro.locals.orders.entries());---<h1>{title}</h1><p>Este {data.property} proviene de un middleware.</p><ul> {orders.map(order => { return <li>{/* haz algo con cada orden */}</li> })}</ul>
Ejemplo: Redactando información sensible
Sección titulada Ejemplo: Redactando información sensibleEl ejemplo a continuación utiliza un middleware para reemplazar “PRIVATE INFO” con la palabra “REDACTED” y así permitirte mostrar HTML modificado en tu página:
export const onRequest = async (context, next) => { const response = await next(); const html = await response.text(); const redactedHtml = html.replaceAll("PRIVATE INFO", "REDACTED");
return new Response(redactedHtml, { status: 200, headers: response.headers });};
Encadenamiento de middleware
Sección titulada Encadenamiento de middlewareMúltiples middlewares pueden ser unidos en un orden específico utilizando sequence()
:
import { sequence } from "astro:middleware";
async function validation(_, next) { console.log("solicitud de validación"); const response = await next(); console.log("respuesta de validación"); return response;}
async function auth(_, next) { console.log("solicitud de autenticación"); const response = await next(); console.log("respuesta de autenticación"); return response;
}
async function greeting(_, next) { console.log("solicitud de saludo"); const response = await next(); console.log("respuesta de saludo"); return response;
}
export const onRequest = sequence(validation, auth, greeting);
Esto resultará en el siguiente orden en la consola:
solicitud de validaciónsolicitud de autenticaciónsolicitud de saludorespuesta de saludorespuesta de autenticaciónrespuesta de validación
Referencia de la API
Sección titulada Referencia de la APIonRequest()
Sección titulada onRequest()Una función exportada requerida desde src/middleware.js
que será llamada antes de renderizar cada página o ruta de la API. Acepta dos argumentos opcionales: context y next(). onRequest()
debe devolver una Response
: ya sea directamente o llamando a next()
.
context
Sección titulada contextUn objeto que incluye información que estará disponible para otros middlewares, rutas de la API y rutas .astro
durante el proceso de renderizado.
Este es un argumento opcional pasado a onRequest()
que puede contener el objeto locals
, así como cualquier propiedad adicional que se comparta durante el renderizado. Por ejemplo, el objeto context
puede incluir cookies utilizadas en la autenticación.
Este es el mismo objeto context
que se pasa a las rutas de la API.
next()
Sección titulada next()Una función que intercepta (lee y modifica) la Response
de una Request
o llama al siguiente middleware en la cadena y devuelve una Response
. Por ejemplo, esta función podría modificar el cuerpo HTML de una respuesta.
Este es un argumento opcional de onRequest()
y puede proporcionar la Response
requerida que devuelve el middleware.
locals
Sección titulada localsUn objeto que contiene datos de una Response
y que se puede manipular dentro del middleware.
Este objeto locals
se reenvía a lo largo del proceso de manejo de la solicitud y está disponible como una propiedad de APIContext
y AstroGlobal
. Esto permite compartir datos entre middlewares, rutas de la API y páginas .astro
. Esto es útil para almacenar datos específicos de la solicitud, como datos de usuario, durante el paso de renderizado.
locals
es un objeto que existe y se destruye dentro de una sola ruta de Astro; cuando se renderiza la página de la ruta, locals
dejará de existir y se creará uno nuevo. La información que necesita persistir a través de múltiples solicitudes de página debe almacenarse en otro lugar.
sequence()
Sección titulada sequence()Una función que acepta funciones de middleware como argumentos y las ejecutará en el orden en que se pasan.
createContext
Sección titulada createContextUna API de bajo nivel para crear un APIContext
, que puede ser utilizado por el middleware de Astro.
Esta función puede ser utilizada por las integraciones/adaptadores para ejecutar programáticamente el middleware de Astro.
trySerializeLocals
Sección titulada trySerializeLocalsUna API de bajo nivel que toma cualquier valor y trata de devolver una versión serializada (un string) de él. Si el valor no se puede serializar, la función lanzará un error en el runtime.