[ PROMPT_NODE_24184 ]
Pages Functions 设计模式
[ SKILL_DOCUMENTATION ]
# 常见模式
## 后台任务 (waitUntil)
响应发送后的非阻塞任务(分析、清理、Webhooks):
typescript
export async function onRequest(ctx: EventContext) {
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; // 立即返回
}
## 中间件与认证
typescript
// functions/_middleware.js (全局) 或 functions/users/_middleware.js (作用域)
export async function onRequest(ctx) {
try { return await ctx.next(); }
catch (err) { return new Response(err.message, { status: 500 }); }
}
// 链式调用: export const onRequest = [errorHandler, auth, logger];
// 认证
async function auth(ctx: EventContext) {
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 与限流
typescript
// CORS 中间件
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;
}
// 限流 (基于 KV)
async function rateLimit(ctx: EventContext) {
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();
}
## 表单、缓存与重定向
typescript
// JSON 与文件上传
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 Respons