跳转到内容

中间件

中间件允许你拦截请求和响应,并在即将渲染页面或端点时动态注入行为。

这也允许你通过修改在所有 Astro 组件和 API 端点中可用的 locals 对象,设置和共享跨端点和页面的请求特定信息。

在 Astro 项目中,无论是 SSG 还是 SSR,都可以使用中间件。

基本用法

  1. 创建 src/middleware.js|ts(或者,你也可以创建 src/middleware/index.js|ts

  2. 在这个文件中,导出一个 onRequest() 函数。注意这里不能是默认导出。

    src/middleware.js
    export function onRequest ({ locals, request }, next) {
    // 从请求拦截响应数据
    // 可选项,通过修改 `locals` 来转换响应
    locals.title = "New title";
    // 返回一个 Response 或者调用 `next()` 的结果
    return next();
    };
  3. 在任何 .astro 文件中,使用 Astro.locals 访问响应数据。

    src/components/Component.astro
    ---
    const data = Astro.locals;
    ---
    <h1>{data.title}</h1>
    <p>这个 {data.property} 来自中间件。</p>

中间件类型

你可以导入并使用 defineMiddleware() 实用函数,以利用类型安全性:

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
// `context` 和 `next` 已自动进行类型定义
export const onRequest = defineMiddleware((context, next) => {
});

相反,如果你正在使用 JsDoc 来利用类型安全性,你可以使用 MiddlewareResponseHandler

src/middleware.js
/**
* @type {import("astro").MiddlewareResponseHandler}
*/
// `context` 和 `next` 已自动进行类型定义
export const onRequest = (context, next) => {
};

为了对 Astro.locals 中的信息进行类型定义,这将在 .astro 文件和中间件代码中为你提供自动完成功能,你需要在 env.d.ts 文件中声明一个全局命名空间:

src/env.d.ts
/// <reference types="astro/client" />
declare namespace App {
interface Locals {
user: {
name: string
},
welcomeTitle: () => string,
orders: Map<string, object>
}
}

然后,在中间件文件中,我们可以利用自动完成和类型安全。

你可以在 Astro.locals 中存储任何类型的数据:字符串,数字,甚至复杂的数据类型,如函数和映射。

src/middleware.js
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 文件中使用这个信息。

src/pages/orders.astro
---
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>

示例:删除敏感信息

下面的示例使用中间件将”私人信息”替换为”已删除”,以便你在页面上渲染修改后的HTML:

src/middleware.js
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
});
};

中间件链式调用

可以使用 sequence() 按指定顺序连接多个中间件:

src/middleware.js
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);

控制台打印结果顺序如下:

Terminal window
验证请求
授权请求
问候请求
问候响应
授权响应
验证响应

API 参考

onRequest()

这是从 src/middleware.js 导出的必需函数,在渲染每个页面或 API 路由之前都会调用。它接受两个可选参数:contextnext()onRequest() 必须返回一个 Response :可以直接返回,或者通过调用 next() 返回。

context

这是一个包含了要在渲染过程中提供给其他中间件,API路由和.astro 路由的信息的对象。

这是传递给 onRequest() 的可选参数,可能包含 locals 对象以及在渲染期间要共享的任何其他属性。例如, context 对象可能包含用于认证的cookies。

这是传递给 API 路由的相同的 context 对象。

next()

这是一个拦截(读取和修改)RequestResponse 或调用链中的”下一个”中间件并返回一个 Response 的函数。例如,这个函数可以修改响应的HTML正文。

这是 onRequest() 的可选参数,并且可能提供由中间件返回的必需的 Response

locals

这是一个包含在 Response 中的数据,可以在中间件内部操作的对象。

locals 对象在请求处理过程中被传递,并作为 APIContextAstroGlobal 的属性可用。这允许在中间件,API路由,和 .astro 页面之间共享数据。这对于在渲染步骤中存储请求特定的数据(例如用户数据)很有用。

locals 是一个在单个 Astro 路由中创建和销毁的对象;当你的路由页面被渲染时,locals 将不再存在,将会创建一个新的。需要在多个页面请求之间保持的信息必须存储在其他地方。

sequence()

这是一个接受中间件函数作为参数,并按照传递的顺序执行它们的函数。

createContext

一个用于创建 APIContext 的底层 API,Astro 中间件可以使用它。

此函数可被集成/适配器用来以编程方式执行 Astro 中间件。

trySerializeLocals

一个接受任何值并尝试返回其序列化版本(字符串)的底层API。如果该值不能被序列化,该函数将抛出运行时错误。