 与 astro/fetch 管线正确落盘 Cookie 与 CDN 缓存头)
Astro 部署 Cloudflare用 astrojs/cloudflare finalize() 与 astro/fetch 管线正确落盘 Cookie 与 CDN 缓存头【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astro导读当 Astro 应用采用 Cloudflare Workers 自定义入口custom entrypoint通常为src/app.ts配合全新的astro/fetch可组合请求管线时如何在返回响应前统一补齐 Cookie 写入与 Cloudflare CDN 缓存默认头是一个容易踩坑的环节。本指南以.changeset/thirty-states-pump.md中记录的astrojs/cloudflareminor 变更为核心讲解新增的finalize()响应处理器、它与cf()和astro()的调用关系、Hono 中间件为何能自动完成同一工作以及静态资产回退与 workerd 预渲染的默认行为。读完你将能在自定义 fetch handler 中写出与官方中间件等价的、安全且完整的响应处理逻辑。背景changeset 记录了什么.changeset/thirty-states-pump.md声明了对astrojs/cloudflare包的一次minor语义化版本变更核心内容是Adds a Cloudflarefinalize()response handler for custom request handlers即为使用astro/fetch管线编写自定义请求处理器的用户新增了名为finalize()的 Cloudflare 侧响应收尾函数。调用它可以在返回来自astro/fetch管线的Response之前统一应用两类信息本次请求期间通过 Astro Cookies API 写入的Set-Cookie响应头Cloudflare CDN 缓存的默认头Cloudflare-CDN-Cache-Control。自定义 fetch handler 的推荐写法changeset 中给出的用法示例是理解本特性最直接的入口。在项目根目录Cloudflare 集成要求自定义入口文件位于src/app.ts中import { astro, FetchState } from astro/fetch; import { cf, finalize } from astrojs/cloudflare/fetch; export default { async fetch(request: Request, env: Env, context: ExecutionContext) { const state new FetchState(request); const asset await cf(state, env, context); if (asset) return asset; return finalize(state, await astro(state)); }, };逐行拆解这段管线的语义new FetchState(request)创建本次请求的状态对象。FetchState在 公共 API 入口 中定义其构造函数会从打包进构建产物的 ambient manifest 读取静态、构建期数据因此无需自行持有 app 或 pipeline 实例仅凭一个裸Request即可构造。await cf(state, env, context)完成 Cloudflare 专属的请求前置准备细节见下文“cf()的前置职责”一节返回Response表示请求已被 ASSETS binding静态资产处理此时应直接短路返回返回undefined则继续进入 Astro 渲染。await astro(state)是astro/fetch提供的核心渲染函数。从源码看它等价地委托给handleRequest(state)见 routing 管线负责把请求派发给匹配的路由页面、端点、重定向或 404/500 回退并生成最终Response。finalize(state, ...)对渲染结果做收尾然后return给运行时。finalize() 做了什么从源码看收尾逻辑finalize的实现非常简短位于 Cloudflare 集成 fetch.ts/** Applies cookies and Cloudflare CDN cache defaults to an Astro response. */ export function finalize(state: FetchState, response: Response): Response { return applyCloudflareResponseHeaders(response, state.cookies.consume(), cacheProviderEnabled); }它把三样东西交给applyCloudflareResponseHeadersresponseAstro或其他上游已经产出的响应state.cookies.consume()本次请求期间通过AstroCookies写入的Set-Cookie头集合一次性取出并消费cacheProviderEnabled来自虚拟模块virtual:astro-cloudflare:config的构建期布尔标志。CDN 缓存默认头的底层规则真正的头部加工逻辑在 utils/response.ts若响应中已经存在Set-Cookie头则把它们逐个追加到目标响应上只有当cacheProviderEnabled true且响应尚未声明Cloudflare-CDN-Cache-Control时才会补上默认值Cloudflare-CDN-Cache-Control: no-store。源码注释解释了原因Cloudflare 的 Worker 缓存默认会把未声明缓存意图的 GET 响应缓存至多两小时因此需要no-store兜底以免无意中被缓存该函数刻意用try/catch处理从 Workers Cache API 取出的响应头不可变这一边界情况首次修改抛错前未发生任何变更于是退化为基于new Response(response.body, response)重建一个可写头的响应后重新应用。cacheProviderEnabled 何时为 truecacheProviderEnabled并非恒为真它由构建配置驱动。从 Cloudflare 集成入口 可以看到needsWorkerCache config.cache?.provider?.name cloudflare随后在 同一文件 L454 注入为cacheProviderEnabled: needsWorkerCache。也就是说只有在astro.config中把 cache provider 配置为 Cloudflare 时finalize()才会对未声明缓存头的响应附加no-store默认值未启用该 provider 时finalize()只负责 Cookie 透传。cf() 的前置职责与静态资产回退示例中的cf()承担了比finalize()更重的前置工作。从 fetch.ts 的实现看它依次完成懒初始化ensureInitialized()延迟到首次调用才执行setGetEnv(...)与createApp()。源码注释说明这样设计是为了避免循环依赖崩溃——自定义fetchFile静态导入本模块时fetch.ts → astro/app/entrypoint → virtual:astro:fetchable → 用户 worker → fetch.ts形成的环会被打破SESSION KV binding 注入通过injectSessionBinding(app.manifest, env)让会话功能可感知 Cloudflare 的 KV 存储静态资产优先matchStaticAsset(...)命中public/等静态文件时直接返回资产响应无匹配路由时回退到 ASSETS bindinghasMatchingRoute(state)用state.routeData?.pattern与原始 pathname 比对以此区分真实命中的 404 路由与兜底的 404 回退路由后者把机会让给 ASSETS binding 处理——这正是 changeset 所述无 Astro 路由匹配时回退静态资产的具体实现补齐请求上下文向state.locals注入cfContext、设置客户端地址、把ctx.waitUntil挂到renderOptions.waitUntil并为错误页提供createErrorPageFetch(env)。完成上述步骤后返回undefined示意调用方继续执行 Astro 渲染。Hono 场景middleware 自动应用并非所有自定义入口都手动拼装管线。若你使用astro/hono的 Hono 组合方式则无需手写finalize()——Cloudflare 的 Hono 中间件 会自动完成整套等价逻辑。官方推荐写法为import { Hono } from hono; import { actions, middleware, pages, i18n } from astro/hono; import { cf } from astrojs/cloudflare/hono; const app new Hono{ Bindings: Env }(); app.use(cf()); app.use(actions()); app.use(middleware()); app.use(pages()); app.use(i18n()); export default app;cf()中间件hono.ts L71-L89的实现值得注意它通过 duck-typing 的 Hono contextreq.raw、env、executionCtx工作运行时不从hono导入任何符号避免在 Worker 环境引入额外运行时依赖getFetchState()优先复用已缓存在 context 上的FetchState键为FETCH_STATE_KEY没有则基于new FetchState(context.req.raw)新建并回写 context中间件内部直接复用cfFetch即上文cf()命中静态资产即返回资产响应否则await next()让后续actions()/middleware()/pages()/i18n()依次执行链结束后调用finalize(state, context.res)。由于 Hono 的 response setter 会 clone 响应并恢复当前响应上的 cookies若finalize产生了新响应对象中间件会把Set-Cookie头先取出、赋给新context.res后再逐条追加回去确保 Cookie 不被 setter 丢弃。这正是 changeset 中astrojs/cloudflare/hono中间件会自动应用这些响应头一句话背后的完整机制。其他行为变更workerd 预渲染与默认入口changeset 末尾还补充了两条配套行为可视为同一批 minor 变更的完整性声明静态资产回退Cloudflare 自定义入口custom entrypoint在没有 Astro 路由匹配请求时会回退到静态资产即上文中fallbackToAssets与 ASSETS binding 的配合workerd 预渲染默认入口当在 workerd 运行时中执行预渲染prerendering时会使用默认的 server entrypoint而不是自定义入口。从仓库结构看这一组新 API 的导出位于 packages/integrations/cloudflare/src/fetch.ts提供cf与finalize与 packages/integrations/cloudflare/src/hono.ts提供自动化的cf()中间件它们在包发布后以astrojs/cloudflare/fetch与astrojs/cloudflare/hono两个子路径对外暴露。使用前提与注意事项基于仓库源码可以总结出以下适用前提供你在实际项目中判断是否采用finalize()仅适用于自定义 fetch handler / Hono 组合管线。传统astro dev/默认 server 入口并不需要手动调用finalize()utils/handler.ts中的默认处理路径同样会调用applyCloudflareResponseHeaders见 handler.ts收尾逻辑由框架内置完成务必在cf()之后、return之前调用。若遗漏finalize()自定义管线产出的响应将缺少 Cookie 与 CDN 缓存默认头而把finalize()放在cf()返回资产的分支之前也没有意义——资产分支应直接return assetno-store默认头仅在启用 Cloudflare cache provider 时生效config.cache.provider.name cloudflare且不会覆盖你主动声明的Cloudflare-CDN-Cache-Control头懒初始化与不可变头处理都是刻意的设计。不要在模块顶层执行createApp()也不要假设任何Response的 header 都可直接改写——这两种边界情况官方均已通过延迟初始化与重建响应策略规避。需要查看本次变更的原始记录可回溯 changeset 文件想继续深入astro/fetch管线中astro、pages、middleware、actions、i18n、cache、sessions等其余可组合函数可阅读 astro/fetch 公共入口FetchState的完整公开契约则定义于 fetch-state.ts。【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考