返回介绍

中间件

发布于 2024-06-05 21:19:57 字数 5950 浏览 0 评论 0 收藏 0

中间件允许你拦截请求和响应,并在即将渲染页面或端点时动态注入行为。对于所有预渲染的页面,这种渲染发生在构建时,但对于按需渲染的页面,这种渲染发生在请求路由时。

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

基本用法

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

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

    src/middleware.js
    export function onRequest (context, next) {
        // 拦截一个请求里的数据
        // 可选地修改 `locals` 中的属性
        context.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>

context 对象

context 对象包含需要对其他中间件、API 路由和 .astro 路由在渲染过程中可用的信息。

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

context.locals 中存储数据

context.locals 是一个可以在中间件中操作、包含来自 Response 的数据的对象。

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

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

src/middleware.js
export function onRequest (context, next) {
    // 拦截一个请求里的数据
    // 可选地修改 `locals` 中的属性
    context.locals.user.name = "John Wick";
    context.locals.welcomeTitle = () => {
        return "Welcome back " + locals.user.name;
    };


    // 返回一个 Response 或者调用 `next()` 的结果
    return next();
};

然后你可以在任何 .astro 文件中通过 Astro.locals 使用这个信息。

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>

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

示例:删除敏感信息

下面的示例使用中间件将”私人信息”替换为”已删除”,以便你在页面上渲染修改后的 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
    });
};

中间件类型

你可以导入并使用 defineMiddleware() 实用函数来提供类型安全:

src/middleware.ts
import { defineMiddleware } from "astro:middleware";


// `context` 和 `next` 会自动被类型化
export const onRequest = defineMiddleware((context, next) => {


});

或者,如果你使用 JsDoc 来提供类型安全,你可以使用 MiddlewareHandler

src/middleware.js
/**
 * @type {import("astro").MiddlewareHandler}
 */
// `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>
    }
}

然后,在中间件中,你就可以使用自动补全和类型安全了。

中间件链式调用

可以使用 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);

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

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

错误页面

即使找不到匹配的路由,中间件也会尝试为所有按需渲染的页面运行。这包括 Astro 的默认(空白)404 页面和任何自定义 404 页面。然而,是否运行该代码取决于适配器。一些适配器可能会提供特定平台的错误页面。

在提供 500 错误页面之前,中间件也会尝试运行,包括自定义的 500 页面,除非服务器错误发生在中间件本身的执行中。如果你的中间件没有成功运行,那么你将无法访问 Astro.locals 来渲染你的 500 页面。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文