2.3 API 路由 Route Handlers


2.3 API 路由(Route Handlers)

本节摘要:全栈应用需要"后端接口"——被外部调用、或客户端组件获取数据。Next.js 用 route.ts 定义 API 路由(Route Handlers)。本节讲清 GET/POST 的写法、请求与响应处理、动态路由参数,以及"何时用 API 路由、何时用 Server Actions"的选择。

上手前先明确

阅读完本节,你应当能够:

  1. 用 route.ts 定义 GET/POST 接口
  2. 读取请求参数(查询、路径、JSON 体)
  3. 返回 JSON 与状态码
  4. 处理动态路由参数
  5. 判断用 API 路由还是 Server Actions

问题与直觉:什么时候需要"自己写后端"

服务器组件能直接查库,为什么还需要 API 路由?因为有三类场景必须暴露 HTTP 接口:

  1. 外部系统调用(移动端 App、第三方服务、前端框架单独部署);
  2. 客户端组件取数(交互组件需要数据,且不想走服务器组件转发);
  3. Webhooks 回调(支付回调、GitHub webhook)。

直觉类比:API 路由是"对外开放的窗口"——服务器组件是"内部直连",API 路由是"门口柜台":外部来客(外部系统)只能通过柜台办事,柜台有权限控制、有台账(日志)。

💡 关键直觉:route.ts 就是"文件的 API 版本"——page.tsx 定义页面(返回 HTML),route.ts 定义接口(返回 JSON)。同样的文件系统路由约定,一个面向浏览器,一个面向程序。

核心原理:Route Handlers 基础

2.1 第一个 API 路由

// app/api/hello/route.ts → GET /api/hello import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ message: "Hello API" }); }

URL 对应app/api/hello/route.ts/api/hello,与 page.tsx 同约定。

2.2 读取请求

// app/api/users/route.ts import { NextRequest, NextResponse } from "next/server"; // GET /api/users?page=2&limit=10 export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const page = searchParams.get("page") ?? "1"; const limit = searchParams.get("limit") ?? "10"; return NextResponse.json({ page, limit }); } // POST /api/users body: {"name":"alice"} export async function POST(request: NextRequest) { const body = await request.json(); // 解析 JSON 体 const name = body.name; // ... 写数据库 return NextResponse.json( { id: 1, name }, { status: 201 } // 创建成功状态码 ); }

2.3 动态路由参数

// app/api/users/[id]/route.ts → /api/users/42 import { NextRequest, NextResponse } from "next/server"; export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const { id } = await params; return NextResponse.json({ userId: id }); }

2.4 控制缓存

API 路由默认不缓存(每次请求都执行)。需要缓存可配置:

export const dynamic = "force-static"; // 构建时静态化 export const revalidate = 3600; // 每小时重新验证

工程实践要点:API 设计规范

3.1 统一响应结构

function ok<T>(data: T) { return NextResponse.json({ code: 0, data }); } function fail(code: number, message: string, status = 400) { return NextResponse.json({ code, message }, { status }); } // 使用 export async function GET() { try { const users = await getUsers(); return ok(users); } catch (e) { return fail(500, "获取用户失败", 500); } }

规范code(业务码,0 成功)+ data(数据)+ message(错误信息),前后端按此约定联调。

3.2 认证与校验

export async function POST(request: NextRequest) { // 1. 鉴权(第三章 3.2 详解) const session = await getSession(); if (!session) return NextResponse.json({ error: "未登录" }, { status: 401 }); // 2. 校验输入 const body = await request.json(); if (!body.name || body.name.length < 2) { return NextResponse.json({ error: "name 至少 2 字符" }, { status: 400 }); } // 3. 业务处理 return NextResponse.json({ ok: true }, { status: 201 }); }

3.3 何时用 API 路由 vs Server Actions

场景 选择
表单提交(页面内) Server Actions(2.8)
客户端组件取数 两者皆可(优先服务器组件转发)
外部系统调用 API 路由
Webhook 回调 API 路由
文件上传 API 路由(表单处理更灵活)
数据变更(增删改) Server Actions(表单场景)

核心判断面向"页面交互"用 Server Actions(更简洁),面向"外部程序"用 API 路由(更标准)

常见误区与排查

误区 现象 正解
路由文件命名错 404 必须是 route.ts
与 page.tsx 同目录 冲突报错 一个目录不能同时有 page 和 route(app/api 例外)
body 解析失败 body 为 undefined await request.json()
返回 404 路径不对 检查目录结构对应 URL
忘记处理 OPTIONS 跨域报错 需要 CORS 时实现 OPTIONS 或配置 headers

动手演练:完整 CRUD API

// app/api/posts/route.ts —— 列表 + 创建 import { NextRequest, NextResponse } from "next/server"; const posts = []; // 演示用内存存储,实际用数据库 export async function GET() { return NextResponse.json(posts); } export async function POST(request: NextRequest) { const body = await request.json(); const post = { id: posts.length + 1, title: body.title, body: body.body }; posts.push(post); return NextResponse.json(post, { status: 201 }); }
// app/api/posts/[id]/route.ts —— 详情 + 删除 export async function GET( _req: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const { id } = await params; const post = posts.find((p) => p.id === Number(id)); if (!post) return NextResponse.json({ error: "不存在" }, { status: 404 }); return NextResponse.json(post); } export async function DELETE( _req: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const { id } = await params; const idx = posts.findIndex((p) => p.id === Number(id)); if (idx === -1) return NextResponse.json({ error: "不存在" }, { status: 404 }); posts.splice(idx, 1); return NextResponse.json({ ok: true }); }

用 curl 测试:`curl 「相关地址请参见官方文档」 -X POST -H "Content-Type: application/json" -d '{"title":"hi"}' 「相关地址请参见官方文档」 app/api 目录里,与前端同项目同部署**。

温故知新

  • Route Handlers:route.ts 定义 API,文件系统路由同约定。
  • HTTP 方法:GET/POST/PUT/DELETE 导出同名 async 函数。
  • 请求读取:NextRequest 的 url/searchParams/json()/动态 params。
  • 响应:NextResponse.json(数据 + 状态码)。
  • 缓存控制:dynamic/revalidate 配置 API 缓存。
  • 选型:页面交互用 Server Actions,外部程序用 API 路由。
  • 规范:统一 code/data/message 结构,鉴权 + 输入校验。

深入理解:API 路由的请求处理流程

一次 API 请求的完整路径

处理顺序:鉴权 → 校验 → 业务 → 响应。任何一步失败都要返回明确的状态码与错误信息,让调用方知道"错在哪"。

进阶:流式响应与文件处理

流式响应(适合大文件下载、SSE 推送):

export async function GET() { const stream = await getLargeData(); return new Response(stream, { headers: { "Content-Type": "text/event-stream" }, }); }

文件上传

export async function POST(request: NextRequest) { const formData = await request.formData(); const file = formData.get("file") as File; // 校验类型与大小后保存 return NextResponse.json({ name: file.name, size: file.size }); }

API 路由的缓存心智:默认不缓存(每次执行)——适合个性化数据;纯读接口可配 revalidate 缓存。写接口(POST/PUT/DELETE)永远不缓存,这是默认行为也符合直觉。

常见问题速答

问:API 路由和 Server Actions 能混用吗?
能,且常见。规则:页面内表单用 Server Actions,外部程序/移动端/Webhook 用 API 路由。两者可调用同一套业务函数,避免逻辑重复。

问:API 路由的返回能是 HTML 吗?
能。new Response(html, { headers: { "Content-Type": "text/html" } }) 或直接返回字符串。但页面用 page.tsx,API 路由一般只返回 JSON。

问:如何保护 API 路由?
每次请求检查认证(auth() 读 session),校验输入,设置 CORS(跨域场景)。与 Server Action 一样,API 路由是网络接口,必须自己鉴权。

问:API 路由会自动生成文档吗?
不会。需要文档时用 OpenAPI 工具(如 next-swagger-doc)或在代码注释中维护。简单项目用 README 维护接口清单即可。

一句话总结

Route Handlers 是"文件的 API 版本"——route.ts 定义接口,与 page.tsx 同路由约定。核心纪律:接口必须自己鉴权与校验,统一返回结构(code/message/data),页面交互用 Server Actions、外部程序用 API 路由。掌握"何时用哪个",就掌握了 Next.js 的后端能力拼图。

动手练习建议

做一个"待办 API"来验证本节知识:用 route.ts 实现 GET(列表)、POST(创建)、DELETE(删除)三个接口,用 curl 分别测试成功与错误场景(参数缺失、资源不存在)。再给接口加上简单鉴权(读一个硬编码 token 头),体会"API 路由必须自己保护"。

进阶练习:把列表接口改成支持 revalidate 缓存(export const revalidate = 60),观察缓存行为;再用 NextResponse.json 统一错误结构(code/message),让调用方容易处理。做完这些,你就掌握了"全栈应用的后端"在 Next.js 中的完整形态。

章节自测

试着不看书回答:route.ts 里 GET 函数返回什么?如何读取路径参数与查询参数?API 路由与 Server Actions 的分工是什么?答对即掌握本节核心。

记住:接口写得好不好,看调用方是否不用猜——状态码语义化、错误信息明确、参数有校验。API 是契约,契约清晰,前后端协作才顺畅。调试接口时先确认三个问题:URL 对不对、方法对不对、参数格式对不对——九成 404/400 都出在这三处。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U