首页 / Next.js 16 入门教程 / Route Handlers

Next.js 16 入门教程

Route Handlers

本教程共 42 篇 · 第 21 篇 · 更新于 2026-07-30 · 约 8 分钟阅读

Next.jsNext.js 16 入门教程Route HandlersAPIHTTPCORS

21. Route Handlers

本节目标:学会使用 Route Handlers 创建自定义 API 端点,掌握请求处理、响应返回、动态路由和 CORS 配置。

Route Handlers 基础

Route Handlers 允许你在 App Router 中创建自定义请求处理端点,使用 Web 标准的 RequestResponse 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() 创建副本。

查询参数

通过 NextRequestnextUrl 属性获取查询参数:

// 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"

使用 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 })
}
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 扩展了标准的 RequestResponse,提供更便捷的方法:

// 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 有一些重要的路由规则:

  1. 不与 page.tsx 共存:同一个路由段不能同时有 route.tspage.tsx
  2. 不参与布局:Route Handler 不包裹在布局中,也不参与客户端导航
  3. 独占 HTTP 方法:每个 route.tspage.tsx 文件占据该路由的所有 HTTP 方法
app/page.tsx        + app/route.ts     -> ✗ 冲突
app/page.tsx        + app/api/route.ts -> ✓ 合法
app/[id]/page.tsx   + app/api/route.ts -> ✓ 合法

小结

  1. 创建方式:在 app 目录下创建 route.ts 文件,导出 HTTP 方法函数
  2. 动态路由:使用 [param] 文件夹语法,通过 params 获取参数
  3. 请求体request.json()request.formData()request.text()
  4. Cookie/Header:使用 cookies()headers() 函数
  5. CORS:手动设置响应头,注意处理 OPTIONS 预检请求
  6. 缓存GET 默认不缓存,可用 dynamic = 'force-static'revalidate 启用