Skip to content

Latest commit

 

History

History
202 lines (153 loc) · 7.08 KB

middleware.mdx

File metadata and controls

202 lines (153 loc) · 7.08 KB
title description i18nReady
中间件
了解如何在 Astro 中使用中间件
true

import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; import { Steps } from '@astrojs/starlight/components';

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

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

基本用法

1. 创建 `src/middleware.js|ts`(或者,你也可以创建 `src/middleware/index.js|ts`)
  1. 在这个文件中,导出一个接收 context 对象onRequest() 函数。注意这里不能是默认导出。

    export function onRequest (context, next) {
        // 拦截一个请求里的数据
        // 可选地修改 `locals` 中的属性
        context.locals.title = "New title";
    
        // 返回一个 Response 或者调用 `next()` 的结果
        return next();
    };
  2. 在任何 .astro 文件中,使用 Astro.locals 访问响应数据。

    ---
    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 页面之间可以共享数据。这对于在渲染步骤中存储请求特定的数据(例如用户数据)很有用。

:::tip[集成属性] 集成可能会在 locals 对象中设置属性和提供一些功能。如果你在使用一个集成,检查它的文档并确认你没有覆盖掉它的属性或者在做重复的工作。 :::

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

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 使用这个信息。

---
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 将不再存在,并会创建一个新的。需要在多个页面请求之间保持的信息必须存储在其他地方。

:::note 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() 实用函数来提供类型安全:

// 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 文件中声明一个全局命名空间:

/// <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);

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

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

错误页面

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

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