中间件
中间件允许你拦截请求和响应,并在即将渲染页面或端点时动态注入行为。
这也允许你通过修改在所有 Astro 组件和 API 端点中可用的 locals 对象,设置和共享跨端点和页面的请求特定信息。
在 Astro 项目中,无论是 SSG 还是 SSR,都可以使用中间件。
基本用法
段落标题 基本用法-
创建
src/middleware.js|ts
(或者,你也可以创建src/middleware/index.js|ts
) -
在这个文件中,导出一个接收
context
对象 的onRequest()
函数。注意这里不能是默认导出。src/middleware.js export function onRequest ({ locals, request }, next) {// 拦截一个请求里的数据// 可选地修改 `locals` 中的属性locals.title = "New title";// 返回一个 Response 或者调用 `next()` 的结果return next();}; -
在任何
.astro
文件中,使用Astro.locals
访问响应数据。src/components/Component.astro ---const data = Astro.locals;---<h1>{data.title}</h1><p>这个 {data.property} 来自中间件。</p>
context
对象
段落标题 context 对象context
对象包含需要对其他中间件、API 路由和 .astro
路由在渲染过程中可用的信息。
它是一个传入 onRequest()
的可选参数,可能包含 locals
对象以及其他在渲染期间共享的任何属性。例如,context
对象可能包含用于认证的 cookies。
在 context.locals
中存储数据
段落标题 在 context.locals 中存储数据context.locals
是一个可以在中间件中操作、包含来自 Response
的数据的对象。
这个 locals
对象在请求处理过程中被传递,并作为 APIContext
和 AstroGlobal
的属性可用。这使得中间件,API 路由和 .astro
页面之间可以共享数据。这对于在渲染步骤中存储请求特定的数据(例如用户数据)很有用。
集成可能会在 locals
对象中设置属性和提供一些功能。如果你在使用一个集成,检查它的文档并确认你没有覆盖掉它的属性或者在做重复的工作。
你可以在 locals
中存储任何类型的数据:字符串,数字,甚至复杂的数据类型,如函数和映射。
export function onRequest ({ locals, request }, next) { // 拦截一个请求里的数据 // 可选地修改 `locals` 中的属性 locals.user.name = "John Wick"; locals.welcomeTitle = () => { return "Welcome back " + locals.user.name; };
// 返回一个 Response 或者调用 `next()` 的结果 return next();};
然后你可以在任何 .astro
文件中通过 Astro.locals
使用这个信息。
---const title = Astro.locals.welcomeTitle();const orders = Array.from(Astro.locals.orders.entries());---<h1>{title}</h1><p>这个 {data.property} 来自中间件。</p><ul> {orders.map(order => { return <li>{/* do something with each order */}</li>; })}</ul>
locals
是一个在单个 Astro 路由中创建和销毁的对象;当你的路由页面渲染完后,locals
将不再存在,并会创建一个新的。需要在多个页面请求之间保持的信息必须存储在其他地方。
locals
的值不能在运行时被覆盖。这样做会有清除用户存储的所有信息的风险。在 dev
模式下 Astro 会进行检查,如果 locals
被覆盖将会抛出错误。
示例:删除敏感信息
段落标题 示例:删除敏感信息下面的示例使用中间件将”私人信息”替换为”已删除”,以便你在页面上渲染修改后的 HTML:
export const onRequest = async (context, next) => { const response = await next(); const html = await response.text(); const redactedHtml = html.replaceAll("私人信息", "已删除");
return new Response(redactedHtml, { status: 200, headers: response.headers });};
中间件类型
段落标题 中间件类型你可以导入并使用 defineMiddleware()
实用函数来提供类型安全:
import { defineMiddleware } from "astro:middleware";
// `context` 和 `next` 会自动被类型化export const onRequest = defineMiddleware((context, next) => {
});
或者,如果你使用 JsDoc 来提供类型安全,你可以使用 MiddlewareHandler
:
/** * @type {import("astro").MiddlewareHandler} */// `context` 和 `next` 会自动被类型化export const onRequest = (context, next) => {
};
要给 Astro.locals
内的信息定义类型,也就是在 .astro
文件和中间件代码中能提供自动不全,在 env.d.ts
文件中声明一个全局命名空间:
/// <reference types="astro/client" />declare namespace App { interface Locals { user: { name: string }, welcomeTitle: () => string, orders: Map<string, object> }}
然后,在中间件中,你就可以使用自动补全和类型安全了。
中间件链式调用
段落标题 中间件链式调用可以使用 sequence()
按指定顺序连接多个中间件:
import { sequence } from "astro:middleware";
async function validation(_, next) { console.log("验证请求"); const response = await next(); console.log("验证响应"); return response;}
async function auth(_, next) { console.log("授权请求"); const response = await next(); console.log("授权响应"); return response;}
async function greeting(_, next) { console.log("问候请求"); const response = await next(); console.log("问候响应"); return response;}
export const onRequest = sequence(validation, auth, greeting);
控制台打印结果顺序如下:
验证请求授权请求问候请求问候响应授权响应验证响应