Cloudflare Pages Functions 实战指南:基于文件路由的 Cloudflare Pages 全栈开发
2026/9/12 17:02:47 网站建设 项目流程

Cloudflare Pages Functions 实战指南:基于文件路由的 Cloudflare Pages 全栈开发

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

本文是 cloudflare-deploy skill 中 Pages Functions 专题的深度展开。Cloudflare Pages Functions 让你在 Pages 静态站点之上直接编写基于 Workers 运行时的无服务器函数,通过文件系统即路由(file-based routing)快速搭建全栈应用。读完本文,你将掌握 Pages Functions 的选型决策、文件路由与动态路由语法、EventContext 与各类 Handler 的用法、KV/D1/R2 等绑定配置,以及中间件、认证、限流、调试和部署的完整实战方案。

Pages Functions 是什么

Cloudflare Pages Functions 是在 Cloudflare Pages 平台上运行的无服务器函数,底层基于 Workers 运行时。它的核心设计是文件即路由:在项目根目录放置一个functions/目录,其中的每个文件自动映射为一个 HTTP 路由,无需额外配置即可让静态站点拥有动态后端能力。

Pages Functions 面向"静态站点 + 动态能力"的组合场景,支持方法级处理器(onRequestGetonRequestPost等)、_middleware.js中间件,以及 KV、D1、R2、Durable Objects、Workers AI、Service Bindings 等全套绑定能力。作为参考,本 skill 的 Product Index 将 Pages Functions 归类在 Compute & Runtime 产品族中,与 Workers、Pages、Durable Objects 并列。

选型决策树:什么时候用 Pages Functions

在动手之前,先根据需求判断是否应该选择 Pages Functions。以下决策树来自 README.md:

Need serverless backend? ├─ Yes, for a static site → Pages Functions ├─ Yes, standalone API → Workers └─ Just static hosting → Pages (no functions) Have existing Worker? ├─ Complex routing logic → Use _worker.js (Advanced Mode) └─ Simple routes → Migrate to /functions (File-Based) Framework-based? ├─ Next.js/SvelteKit/Remix → Uses _worker.js automatically └─ Vanilla/HTML/React SPA → Use /functions

要点解读:

  • 纯静态托管:不需要后端,直接用 Pages 即可,无需引入 Functions;
  • 静态站点需要后端逻辑:如表单处理、鉴权、数据读写,选 Pages Functions;
  • 独立 API 服务:更偏向直接使用 Workers;
  • 已有 Worker 且路由逻辑复杂:可改用_worker.js高级模式(Advanced Mode),自己掌控完整 fetch 流程;
  • 使用框架:Next.js、SvelteKit、Remix 等框架会自动生成_worker.js,你通常不需要手动管理functions/目录;原生 HTML/React SPA 则适合文件式路由。

文件式路由(File-Based Routing)

Pages Functions 的核心机制是文件路径到 URL 的映射。在项目根目录创建functions/目录:

/functions ├── index.js → / ├── api.js → /api ├── users/ │ ├── index.js → /users/ │ ├── [user].js → /users/:user │ └── [[catchall]].js → /users/* └── _middleware.js → runs on all routes

路由规则:

  • index.js对应目录根路径;
  • 结尾斜杠可省略(/users//users等价);
  • 具体路由优先于 catch-all 路由;
  • 若没有函数匹配,则回退到静态资源。

动态路由(Dynamic Routes)

Pages Functions 支持单段与多段两种动态路由语法。

单段参数[param]→ 字符串:匹配单个路径段,参数通过context.params以字符串形式暴露:

// /functions/users/[user].js export function onRequest(context) { return new Response(`Hello ${context.params.user}`); } // Matches: /users/nevi

多段参数[[param]]→ 数组:匹配零个或多个路径段,参数以数组形式暴露:

// /functions/users/[[catchall]].js export function onRequest(context) { return new Response(JSON.stringify(context.params.catchall)); } // Matches: /users/nevi/foobar → ["nevi", "foobar"]

注意[param](单中括号)与[[param]](双中括号)的语义差异:前者匹配单个段,后者匹配多段路径。

函数 API:EventContext 与 Handler

每个函数文件导出一个或多个 handler。handler 接收统一的EventContext对象,其完整结构定义见 api.md:

interface EventContext<Env = any> { request: Request; // Incoming request functionPath: string; // Request path waitUntil(promise: Promise<any>): void; // Background tasks (non-blocking) passThroughOnException(): void; // Fallback to static on error next(input?: Request | string, init?: RequestInit): Promise<Response>; env: Env; // Bindings, vars, secrets params: Record<string, string | string[]>; // Route params ([user] or [[catchall]]) data: any; // Middleware shared state }

各字段职责:

  • request:当前请求对象,读取 headers、body、URL 等;
  • params:路由参数,动态路由一节中[user]/[[catchall]]的取值就在这里;
  • env:绑定的命名空间、环境变量与密钥的入口;
  • next():调用链中的下一个处理器(中间件核心);
  • waitUntil():注册后台任务,不阻塞响应;
  • data:中间件之间共享状态的通道;
  • passThroughOnException():函数抛异常时回退到静态资源。

通用与按方法 Handler

// Generic (fallback for any method) export async function onRequest(ctx: EventContext): Promise<Response> { return new Response('Any method'); } // Method-specific (takes precedence over generic) export async function onRequestGet(ctx: EventContext): Promise<Response> { return Response.json({ message: 'GET' }); } export async function onRequestPost(ctx: EventContext): Promise<Response> { const body = await ctx.request.json(); return Response.json({ received: body }); } // Also: onRequestPut, onRequestPatch, onRequestDelete, onRequestHead, onRequestOptions

通用onRequest作为任意方法的兜底,而onRequestGetonRequestPost等按方法命名的 handler 优先级更高。需要处理 JSON 请求体时,直接await ctx.request.json()即可。

绑定(Bindings)配置与使用

绑定把 Cloudflare 平台的存储、计算与 AI 能力注入到ctx.env中。api.md 给出了完整对照表:

Binding TypeInterfaceConfig KeyUse Case
KVKVNamespacekv_namespacesKey-value cache, sessions, config
D1D1Databased1_databasesRelational data, SQL queries
R2R2Bucketr2_bucketsLarge files, user uploads, assets
Durable ObjectsDurableObjectNamespacedurable_objects.bindingsStateful coordination, websockets
Workers AIAiai.bindingLLM inference, embeddings
VectorizeVectorizeIndexvectorizeVector search, embeddings
Service BindingFetcherservicesWorker-to-worker RPC
Analytics EngineAnalyticsEngineDatasetanalytics_engine_datasetsEvent logging, metrics
Environment VarsstringvarsNon-sensitive config

KV:键值缓存与会话

interface Env { KV: KVNamespace; } export const onRequest: PagesFunction<Env> = async (ctx) => { await ctx.env.KV.put('key', 'value', { expirationTtl: 3600 }); const val = await ctx.env.KV.get('key', { type: 'json' }); const keys = await ctx.env.KV.list({ prefix: 'user:' }); return Response.json({ val }); };

适合存配置、会话与缓存;expirationTtl控制过期时间(秒),type: 'json'可自动反序列化。

D1:关系型 SQL

interface Env { DB: D1Database; } export const onRequest: PagesFunction<Env> = async (ctx) => { const user = await ctx.env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(123).first(); return Response.json(user); };

D1 提供 SQLite 兼容的关系型查询,prepare(...).bind(...)做参数绑定避免注入风险。

R2:对象存储

interface Env { BUCKET: R2Bucket; } export const onRequest: PagesFunction<Env> = async (ctx) => { const obj = await ctx.env.BUCKET.get('file.txt'); if (!obj) return new Response('Not found', { status: 404 }); await ctx.env.BUCKET.put('file.txt', ctx.request.body); return new Response(obj.body); };

适合大文件、用户上传与静态资产,S3 兼容。

Durable Objects:有状态协调

interface Env { COUNTER: DurableObjectNamespace; } export const onRequest: PagesFunction<Env> = async (ctx) => { const stub = ctx.env.COUNTER.get(ctx.env.COUNTER.idFromName('global')); return stub.fetch(ctx.request); };

idFromName('global')按名字取稳定实例,stub.fetch()把请求转发给 DO 实例处理。

Workers AI:LLM 推理

interface Env { AI: Ai; } export const onRequest: PagesFunction<Env> = async (ctx) => { const resp = await ctx.env.AI.run('@cf/meta/llama-3.1-8b-instruct', { prompt: 'Hello' }); return Response.json(resp); };

通过ai.binding配置,直接调用 Workers AI 的模型完成推理。

Service Bindings 与环境变量

interface Env { AUTH: Fetcher; API_KEY: string; } export const onRequest: PagesFunction<Env> = async (ctx) => { // Service binding: forward to another Worker return ctx.env.AUTH.fetch(ctx.request); // Environment variable return Response.json({ key: ctx.env.API_KEY }); };

Service Binding 实现 Worker 到 Worker 的 RPC;vars中定义的非敏感配置直接以字符串读取。

TypeScript 与 wrangler.jsonc 配置

configuration.md 详细说明了类型与配置体系。

TypeScript 设置

推荐用wrangler typeswrangler.jsonc自动生成类型(取代已弃用的@cloudflare/workers-types):

npx wrangler types

命令会生成worker-configuration.d.ts,其中包含基于绑定定义的类型化Env接口:

// functions/api.ts export const onRequest: PagesFunction<Env> = async (ctx) => { // ctx.env.KV, ctx.env.DB, etc. are fully typed return Response.json({ ok: true }); };

若不使用wrangler types,也可手动声明Env接口:

interface Env { KV: KVNamespace; DB: D1Database; API_KEY: string; } export const onRequest: PagesFunction<Env> = async (ctx) => { /* ... */ };

wrangler.jsonc 完整示例

{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "my-pages-app", "pages_build_output_dir": "./dist", "compatibility_date": "2025-01-01", "compatibility_flags": ["nodejs_compat"], "vars": { "API_URL": "https://api.example.com" }, "kv_namespaces": [{ "binding": "KV", "id": "abc123" }], "d1_databases": [{ "binding": "DB", "database_name": "prod-db", "database_id": "xyz789" }], "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "my-bucket" }], "durable_objects": { "bindings": [{ "name": "COUNTER", "class_name": "Counter", "script_name": "counter-worker" }] }, "services": [{ "binding": "AUTH", "service": "auth-worker" }], "ai": { "binding": "AI" }, "vectorize": [{ "binding": "VECTORIZE", "index_name": "my-index" }], "analytics_engine_datasets": [{ "binding": "ANALYTICS" }] }

关键字段说明:

  • pages_build_output_dir:构建产物目录,Pages Functions 就在此输出中被识别;
  • compatibility_date/compatibility_flags:指定运行时兼容日期与特性开关,如nodejs_compat可启用 Node.js 兼容 API;
  • vars:非敏感环境变量;
  • kv_namespacesd1_databasesr2_buckets等:各绑定命名空间,binding字段必须与代码中ctx.env的键名完全一致(大小写敏感)

环境覆盖(Environment Overrides)

配置遵循"顶层 → 本地开发、env.preview→ 预览、env.production→ 生产"的覆盖层级:

{ "vars": { "API_URL": "http://localhost:8787" }, "env": { "production": { "vars": { "API_URL": "https://api.example.com" } } } }

注意:一旦在某环境里覆盖varskv_namespacesd1_databases等数组/对象字段,必须把其中所有项在该环境内完整重新定义——这些配置不可继承

本地密钥(.dev.vars)

.dev.vars仅用于本地开发,不会被部署到线上:

# .dev.vars (add to .gitignore) SECRET_KEY="my-secret-value"

本地通过ctx.env.SECRET_KEY读取。生产环境的密钥需要用wrangler pages secret put单独设置:

echo "value" | npx wrangler pages secret put SECRET_KEY --project-name=my-app

静态配置文件

Pages 支持三类静态配置文件(见 configuration.md):

_routes.json—— 自定义路由包含/排除规则:

{ "version": 1, "include": ["/api/*"], "exclude": ["/static/*"] }

_headers—— 静态资源响应头:

/static/* Cache-Control: public, max-age=31536000

_redirects—— 路径重定向:

/old /new 301

常见模式:中间件、认证、限流与后台任务

patterns.md 汇总了高频率实战模式。

中间件与认证

functions/_middleware.js作用于全局所有路由,functions/users/_middleware.js则只作用于users路由树。中间件通过ctx.next()把请求交给后续处理器:

// functions/_middleware.js (global) or functions/users/_middleware.js (scoped) export async function onRequest(ctx) { try { return await ctx.next(); } catch (err) { return new Response(err.message, { status: 500 }); } } // Chained: export const onRequest = [errorHandler, auth, logger];

认证中间件——校验 Bearer Token,把用户信息写入ctx.data供后续共享:

async function auth(ctx: EventContext<Env>) { const token = ctx.request.headers.get('authorization')?.replace('Bearer ', ''); if (!token) return new Response('Unauthorized', { status: 401 }); const session = await ctx.env.KV.get(`session:${token}`); if (!session) return new Response('Invalid', { status: 401 }); ctx.data.user = JSON.parse(session); return ctx.next(); }

CORS 与基于 KV 的限流

// CORS middleware const cors = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST' }; export async function onRequestOptions() { return new Response(null, { headers: cors }); } export async function onRequest(ctx) { const res = await ctx.next(); Object.entries(cors).forEach(([k, v]) => res.headers.set(k, v)); return res; } // Rate limiting (KV-based) async function rateLimit(ctx: EventContext<Env>) { const ip = ctx.request.headers.get('CF-Connecting-IP') || 'unknown'; const count = parseInt(await ctx.env.KV.get(`rate:${ip}`) || '0'); if (count >= 100) return new Response('Rate limited', { status: 429 }); await ctx.env.KV.put(`rate:${ip}`, (count + 1).toString(), { expirationTtl: 3600 }); return ctx.next(); }

限流思路:以CF-Connecting-IP作为客户端标识,在 KV 中维护计数,超过阈值返回 429,expirationTtl: 3600让计数每小时自动过期。

表单、缓存与重定向

// JSON & file upload export async function onRequestPost(ctx) { const ct = ctx.request.headers.get('content-type') || ''; if (ct.includes('application/json')) return Response.json(await ctx.request.json()); if (ct.includes('multipart/form-data')) { const file = (await ctx.request.formData()).get('file') as File; await ctx.env.BUCKET.put(file.name, file.stream()); return Response.json({ uploaded: file.name }); } } // Cache API export async function onRequest(ctx) { let res = await caches.default.match(ctx.request); if (!res) { res = new Response('Data'); res.headers.set('Cache-Control', 'public, max-age=3600'); ctx.waitUntil(caches.default.put(ctx.request, res.clone())); } return res; } // Redirects export async function onRequest(ctx) { if (new URL(ctx.request.url).pathname === '/old') { return Response.redirect(new URL('/new', ctx.request.url), 301); } return ctx.next(); }

后台任务(waitUntil)

waitUntil注册的 Promise 在响应返回后继续执行,适合埋点、清理、Webhook 通知等非阻塞任务:

export async function onRequest(ctx: EventContext<Env>) { const res = Response.json({ success: true }); ctx.waitUntil(ctx.env.KV.put('last-visit', new Date().toISOString())); ctx.waitUntil(Promise.all([ ctx.env.ANALYTICS.writeDataPoint({ event: 'view' }), fetch('https://webhook.site/...', { method: 'POST' }) ])); return res; // Returned immediately }

单元测试

用 Vitest +cloudflare:test直接对 handler 做单元测试:

import { env } from 'cloudflare:test'; import { it, expect } from 'vitest'; import { onRequest } from '../functions/api'; it('returns JSON', async () => { const req = new Request('http://localhost/api'); const ctx = { request: req, env, params: {}, data: {} } as EventContext; const res = await onRequest(ctx); expect(res.status).toBe(200); });

集成测试则用wrangler pages dev起本地服务,配合 Playwright/Cypress 做端到端验证。

高级模式(Advanced Mode:_worker.js)

当路由逻辑复杂、或项目由框架生成(Next.js/SvelteKit/Remix)时,可放弃functions/目录改用_worker.js,获得完整的 Worker 形态控制权。此时静态资源通过env.ASSETS.fetch()访问(api.md):

interface Env { ASSETS: Fetcher; KV: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); if (url.pathname.startsWith('/api/')) { return Response.json({ data: await env.KV.get('key') }); } return env.ASSETS.fetch(request); // Fallback to static } } satisfies ExportedHandler<Env>;

何时使用高级模式:已有 Worker、框架自动生成(Next.js/SvelteKit)、需要自定义路由逻辑。

本地开发与部署

来自 configuration.md 的完整命令:

# Dev server npx wrangler pages dev ./dist # With bindings npx wrangler pages dev ./dist --kv=KV --d1=DB=db-id --r2=BUCKET # Durable Objects (2 terminals) cd do-worker && npx wrangler dev cd pages-project && npx wrangler pages dev ./dist --do COUNTER=Counter@do-worker # Deploy npx wrangler pages deploy ./dist npx wrangler pages deploy ./dist --branch preview # Download config npx wrangler pages download config my-project

要点:

  • 开发时先构建静态站点到pages_build_output_dir指定的目录(如./dist)再pages dev
  • 本地联调绑定可通过--kv--d1--r2--do等参数挂载;
  • 部署支持--branch指定预览分支;
  • 部署前务必确认已认证(参考 SKILL.md 中的npx wrangler whoami检查),生产密钥使用wrangler pages secret put设置。

错误诊断与调试

gotchas.md 给出了高频问题速查表:

SymptomLikely CauseSolution
Function not invokingWrong/functionslocation, wrong extension, or_routes.jsonexcludes pathCheckpages_build_output_dir, use.js/.ts, verify_routes.json
ctx.env.BINDINGundefinedBinding not configured or name mismatchAdd towrangler.jsonc, verify exact name (case-sensitive), redeploy
TypeScript errors onctx.envMissing type definitionRunwrangler typesor defineinterface Env {}
Middleware not runningWrong filename/location or missingctx.next()Name exactly_middleware.js, exportonRequest, callctx.next()
Secrets missing in production.dev.varsnot deployed.dev.varsis local only - set production secrets via dashboard orwrangler secret put
Type mismatch on bindingWrong interface typeSee bindings table for correct types
"KV key not found" but existsKey in wrong namespace or envVerify namespace binding, check preview vs production env
Function times outSynchronous wait or missingawaitAll I/O must be async/await, usectx.waitUntil()for background tasks

调试手段包括:

// Console logging export async function onRequest(ctx) { console.log('Request:', ctx.request.method, ctx.request.url); const res = await ctx.next(); console.log('Status:', res.status); return res; }
# Stream real-time logs npx wrangler pages deployment tail npx wrangler pages deployment tail --status error
// Source maps (wrangler.jsonc) { "upload_source_maps": true }

平台限制

ResourceFreePaid
CPU time10ms50ms
Memory128 MB128 MB
Script size10 MB compressed10 MB compressed
Env vars5 KB per var, 64 max5 KB per var, 64 max
Requests100k/dayUnlimited ($0.50/million)

从限制表可以推断:函数内所有 I/O 必须保持异步(async/await),避免同步阻塞导致 CPU 时间耗尽;脚本体积应控制在压缩后 10MB 以内,因此要精简依赖以降低冷启动时间。

最佳实践

性能:最小化依赖(减小冷启动)、按用途选存储(KV 做缓存、D1 做关系型、R2 存大文件)、设置Cache-Control头、批量数据库操作、优雅处理错误。

安全:绝不提交密钥(用.dev.vars+ gitignore)、校验输入、写入数据库前做清洗、实现认证中间件、设置 CORS 头、按 IP 限流。

迁移指南

Workers → Pages Functions:

  • export default { fetch(req, env) {} }export function onRequest(ctx) { const { request, env } = ctx; }
  • 复杂路由改用_worker.js,静态文件通过env.ASSETS.fetch(request)访问。

其他平台 → Pages:

  • 文件式路由:/functions/api/users.js对应/api/users
  • 动态路由用[param]而非:param
  • 用 Workers API 替代 Node.js 依赖,或添加nodejs_compat兼容标志。

阅读顺序与延伸参考

如果你初次接触 Pages Functions,推荐按以下顺序阅读本 skill 内的专题文档:

  1. pages-functions/README.md —— 概览、路由、选型决策树;
  2. configuration.md —— TypeScript 设置、wrangler.jsonc、绑定配置;
  3. api.md —— EventContext、Handler、绑定参考;
  4. patterns.md —— 中间件、认证、CORS、限流、缓存;
  5. gotchas.md —— 常见错误、调试、限制。

需要速查时:绑定对照表看 api.md,错误诊断看 gotchas.md,TypeScript 设置看 configuration.md。

此外,Pages 平台总览、Workers 运行时 API 与 D1 数据库集成 也是与本主题直接相关的延伸资料,可结合阅读。

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询