Route Handlers
本教程共 42 篇 · 第 21 篇 · 更新于 2026-07-30 · 约 8 分钟阅读
21. Route Handlers
本节目标:学会使用 Route Handlers 创建自定义 API 端点,掌握请求处理、响应返回、动态路由和 CORS 配置。
Route Handlers 基础
Route Handlers 允许你在 App Router 中创建自定义请求处理端点,使用 Web 标准的 Request 和 Response API。
与 Pages Router API Routes 的关系
Route Handlers 是 Pages Router 中 API Routes 的替代品。两者功能相同但语法不同,不需要同时使用。
文件约定
在 app 目录下创建 route.ts(或 route.js)文件:
// app/api/route.ts
export async function GET(request: Request) {
return Response.json({ message: 'Hello World' })
}
Route Handler 可以嵌套在 app 目录的任意位置,但不能与同级的 page.tsx 共存。
支持的 HTTP 方法
Route Handler 支持导出以下 HTTP 方法对应的函数:
// app/api/route.ts
export async function GET(request: Request) {}
export async function HEAD(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function DELETE(request: Request) {}
export async function PATCH(request: Request) {}
// 如果不定义 OPTIONS,Next.js 会自动实现并设置 Allow 头
export async function OPTIONS(request: Request) {}
如果调用了未定义的方法,Next.js 会返回 405 Method Not Allowed。
动态路由
Route Handler 支持动态路由参数:
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
return Response.json({ userId: id })
}
// app/api/blog/[slug]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ slug: string }> }
) {
const { slug } = await params
const post = await getPostBySlug(slug)
return Response.json(post)
}
Next.js 16 写法 vs 旧版写法
Next.js 15+ 中
params是 Promise,需要await。旧版本中params是直接的对象。
使用 Route Context Helper 获得类型
Next.js 提供全局的 RouteContext 辅助类型:
// app/api/users/[id]/route.ts
import type { NextRequest } from 'next/server'
export async function GET(
_req: NextRequest,
ctx: RouteContext<'/api/users/[id]'>
) {
const { id } = await ctx.params
return Response.json({ userId: id })
}
处理请求体
JSON 请求体
// app/api/users/route.ts
export async function POST(request: Request) {
const body = await request.json()
// body 已经是解析后的对象
return Response.json({ received: body })
}
FormData 请求体
// app/api/upload/route.ts
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
纯文本请求体
// app/api/webhook/route.ts
export async function POST(request: Request) {
const text = await request.text()
// 处理 webhook 载荷
return new Response('Success!', { status: 200 })
}
请求体只能读取一次
如果需要多次读取请求体,使用
request.clone()创建副本。
查询参数
通过 NextRequest 的 nextUrl 属性获取查询参数:
// app/api/search/route.ts
import type { NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
const page = searchParams.get('page') || '1'
return Response.json({ query, page })
}
访问 /api/search?query=nextjs&page=2 时,query 为 "nextjs",page 为 "2"。
Cookie 操作
使用 cookies API
// app/api/route.ts
import { cookies } from 'next/headers'
export async function GET() {
const cookieStore = await cookies()
// 读取 cookie
const token = cookieStore.get('token')
// 设置 cookie
cookieStore.set('session', 'abc123', {
httpOnly: true,
secure: true,
maxAge: 60 * 60 * 24 * 7, // 7 天
path: '/',
})
// 删除 cookie
cookieStore.delete('old-cookie')
return Response.json({ token: token?.value })
}
通过 Response 头设置 Cookie
export async function GET() {
return new Response('Hello', {
status: 200,
headers: {
'Set-Cookie': 'theme=dark; Path=/; HttpOnly; Secure',
},
})
}
Header 操作
读取请求头
// app/api/route.ts
import { headers } from 'next/headers'
export async function GET() {
const headersList = await headers()
const userAgent = headersList.get('user-agent')
const authorization = headersList.get('authorization')
return Response.json({ userAgent })
}
设置响应头
export async function GET() {
return new Response('Hello', {
status: 200,
headers: {
'Content-Type': 'text/plain',
'Cache-Control': 'public, max-age=3600',
},
})
}
CORS 配置
跨域资源共享(CORS)需要在响应头中添加特定的允许字段:
// app/api/route.ts
export async function GET(request: Request) {
return new Response('Hello, CORS!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
// 处理预检请求
export async function OPTIONS(request: Request) {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
},
})
}
多个 Route Handler 的 CORS 配置
如果需要为多个 Route Handler 添加 CORS 头,可以使用 Proxy 或在
next.config.ts中配置headers。
缓存控制
Route Handler 的 GET 方法默认不缓存。可以通过配置启用缓存:
// app/api/data/route.ts
export const dynamic = 'force-static'
export async function GET() {
const data = await fetch('https://api.example.com/data').then((res) =>
res.json()
)
return Response.json(data)
}
使用 revalidate 选项可以实现增量静态再生(ISR):
// app/api/posts/route.ts
export const revalidate = 60 // 每 60 秒重新验证
export async function GET() {
const data = await fetch('https://api.example.com/posts')
const posts = await data.json()
return Response.json(posts)
}
Note如果启用了 Cache Components(
cacheComponents: true),export const revalidate路由段配置会被移除。此时请在 GET 函数内使用"use cache"指令配合cacheLife实现重新验证。
重定向
// app/api/old-route/route.ts
import { redirect } from 'next/navigation'
export async function GET() {
redirect('https://example.com/new-path')
}
NextRequest 和 NextResponse
Next.js 扩展了标准的 Request 和 Response,提供更便捷的方法:
// app/api/route.ts
import { NextRequest, NextResponse } from 'next/server'
export async function GET(request: NextRequest) {
const nextUrl = request.nextUrl
if (nextUrl.searchParams.get('redirect')) {
return NextResponse.redirect(new URL('/', request.url))
}
if (nextUrl.searchParams.get('rewrite')) {
return NextResponse.rewrite(new URL('/other-page', request.url))
}
return NextResponse.json({ pathname: nextUrl.pathname })
}
流式响应
Route Handler 支持流式响应,常用于 AI 内容生成:
// app/api/stream/route.ts
export async function GET() {
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
controller.enqueue(encoder.encode('第一段内容\n'))
await new Promise((r) => setTimeout(r, 1000))
controller.enqueue(encoder.encode('第二段内容\n'))
await new Promise((r) => setTimeout(r, 1000))
controller.enqueue(encoder.encode('第三段内容\n'))
controller.close()
},
})
return new Response(stream, {
headers: { 'Content-Type': 'text/plain' },
})
}
路由解析规则
Route Handler 有一些重要的路由规则:
- 不与 page.tsx 共存:同一个路由段不能同时有
route.ts和page.tsx - 不参与布局:Route Handler 不包裹在布局中,也不参与客户端导航
- 独占 HTTP 方法:每个
route.ts或page.tsx文件占据该路由的所有 HTTP 方法
app/page.tsx + app/route.ts -> ✗ 冲突
app/page.tsx + app/api/route.ts -> ✓ 合法
app/[id]/page.tsx + app/api/route.ts -> ✓ 合法
小结
- 创建方式:在
app目录下创建route.ts文件,导出 HTTP 方法函数 - 动态路由:使用
[param]文件夹语法,通过params获取参数 - 请求体:
request.json()、request.formData()、request.text() - Cookie/Header:使用
cookies()和headers()函数 - CORS:手动设置响应头,注意处理
OPTIONS预检请求 - 缓存:
GET默认不缓存,可用dynamic = 'force-static'或revalidate启用