Astro 的中间件是如何工作的?
中间件是什么
Astro 中间件是一段在请求到达页面之前执行的代码。它可以拦截请求、修改响应、或者直接返回结果——本质上是一个运行在服务端的请求拦截器。
中间件定义在 src/middleware.ts 中,导出一个 onRequest 函数:
typescriptimport { defineMiddleware } from 'astro:middleware'; export const onRequest = defineMiddleware(async (context, next) => { // 请求到达页面之前 const response = await next(); // 页面渲染之后,可修改响应 return response; });
context 提供了 request、url、cookies、locals 等属性;next() 将请求传递给下一个处理环节并返回响应。
执行时机与渲染模式的关系
面试常考的一个点:中间件在不同渲染模式下的行为不同。对于按需渲染(SSR)的页面,中间件在每次请求时运行;对于预渲染(SSG)的页面,中间件在构建时运行。这意味着如果你的页面全部是静态的,中间件只在 build 阶段执行一次,运行时不会触发。
常见使用场景
认证守卫是最典型的场景。在中间件中检查 cookie 或 header 中的 token,未认证则重定向到登录页,通过后将用户信息存入 context.locals,后续页面直接从 Astro.locals 读取。
重定向管理适合在这里统一处理旧路径到新路径的映射,避免在页面组件里分散写 redirect 逻辑。
国际化也是常见用途:从 URL 路径或 Accept-Language 头检测语言偏好,不匹配时重定向到正确的语言版本,匹配则将 locale 写入 locals 供页面使用。
链式中间件
Astro 提供了 sequence 函数将多个中间件串联,按顺序依次执行:
typescriptimport { sequence } from 'astro:middleware'; import { auth, i18n, log } from './middleware'; export const onRequest = sequence(auth, i18n, log);
执行顺序就是参数顺序:auth 先于 i18n 先于 log。每个中间件都可以选择是否调用 next()。
locals 的作用
context.locals 是中间件和页面之间的数据桥梁。中间件写入的数据在页面的 Astro.locals 中可以直接读取,这是 Astro 推荐的跨层传数据方式,避免了全局状态或重复请求。
追问:中间件里能直接返回 Response 吗
可以。不调用 next() 而直接 return new Response(...) 就能短路整个请求链,页面不会渲染。这在认证失败、限流、维护模式等场景下很有用。
另一个追问:中间件能修改响应体吗?能,但需要在 next() 返回之后 clone 一份 Response 再修改,因为 Response 对象是不可变的。
typescriptexport const onRequest = defineMiddleware(async (context, next) => { const response = await next(); const body = await response.text(); const modified = body.replace('old', 'new'); return new Response(modified, { status: response.status, headers: response.headers, }); });
Astro 中间件的核心思路和 Express 的 middleware 一脉相承:拦截-处理-传递。理解了请求生命周期中它的位置,以及 locals 的数据传递机制,面试中相关问题基本都能应对。