news 2026/7/6 14:13:13

05-服务端渲染与元框架——06. API Routes - 服务端 API

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
05-服务端渲染与元框架——06. API Routes - 服务端 API

06. API Routes - 服务端 API

概述

API Routes 允许在 Next.js 应用中创建服务端 API 端点,实现前后端一体化。你可以在app/api/目录下定义路由处理器,处理 HTTP 请求并返回响应。

维度内容
What在 Next.js 应用中创建 API 端点
Why前后端一体化,无需单独部署 API 服务
When需要服务端逻辑、代理请求、处理表单等
Whereapp/api/目录下的route.js文件
Who需要后端功能的开发者
Howexport async function GET(request) { return Response.json(data) }

1. API Routes 基础

1.1 创建 API 路由

// app/api/hello/route.js export async function GET() { return Response.json({ message: 'Hello World!' }); } // 访问: /api/hello

1.2 HTTP 方法

// app/api/users/route.js // GET - 获取用户列表 export async function GET() { const users = await db.user.findMany(); return Response.json(users); } // POST - 创建用户 export async function POST(request) { const body = await request.json(); const user = await db.user.create({ data: body }); return Response.json(user, { status: 201 }); } // PUT - 更新用户(全量) export async function PUT(request) { const body = await request.json(); const user = await db.user.update({ where: { id: body.id }, data: body, }); return Response.json(user); } // PATCH - 更新用户(部分) export async function PATCH(request) { const body = await request.json(); const user = await db.user.update({ where: { id: body.id }, data: body, }); return Response.json(user); } // DELETE - 删除用户 export async function DELETE(request) { const { searchParams } = new URL(request.url); const id = searchParams.get('id'); await db.user.delete({ where: { id } }); return Response.json({ success: true }); }

2. 动态 API 路由

2.1 动态参数

// app/api/users/[id]/route.js export async function GET(request, { params }) { const { id } = await params; const user = await db.user.findUnique({ where: { id } }); if (!user) { return Response.json({ error: '用户不存在' }, { status: 404 }); } return Response.json(user); } export async function PUT(request, { params }) { const { id } = await params; const body = await request.json(); const user = await db.user.update({ where: { id }, data: body, }); return Response.json(user); } export async function DELETE(request, { params }) { const { id } = await params; await db.user.delete({ where: { id } }); return Response.json({ success: true }); }

2.2 捕获所有路由

// app/api/docs/[...slug]/route.js export async function GET(request, { params }) { const { slug } = await params; // slug 是数组: ['getting-started', 'installation'] const path = slug.join('/'); const content = await getDocContent(path); return Response.json({ path, content }); } // 访问 /api/docs/getting-started/installation → slug = ['getting-started', 'installation']

2.3 可选捕获路由

// app/api/blog/[[...slug]]/route.js export async function GET(request, { params }) { const { slug } = await params; if (!slug) { // /api/blog → 返回所有文章 const posts = await db.post.findMany(); return Response.json(posts); } // /api/blog/post-1 → 返回单篇文章 const post = await db.post.findUnique({ where: { slug: slug[0] } }); return Response.json(post); }

3. 请求处理

3.1 获取请求参数

// app/api/search/route.js export async function GET(request) { const { searchParams } = new URL(request.url); const q = searchParams.get('q'); const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '10'); const results = await db.post.findMany({ where: { title: { contains: q } }, skip: (page - 1) * limit, take: limit, }); return Response.json(results); }

3.2 获取请求体

// app/api/posts/route.js export async function POST(request) { // JSON 请求体 const body = await request.json(); // 表单数据 const formData = await request.formData(); const title = formData.get('title'); const content = formData.get('content'); // 文本请求体 const text = await request.text(); // 原始二进制数据 const blob = await request.blob(); return Response.json({ received: true }); }

3.3 请求头

// app/api/protected/route.js export async function GET(request) { const authHeader = request.headers.get('Authorization'); const userAgent = request.headers.get('User-Agent'); const contentType = request.headers.get('Content-Type'); if (!authHeader) { return Response.json({ error: '未授权' }, { status: 401 }); } return Response.json({ userAgent, contentType }); }

4. 响应处理

4.1 基本响应

// 成功响应 return Response.json({ data: 'success' }); // 自定义状态码 return Response.json({ message: 'Created' }, { status: 201 }); // 错误响应 return Response.json({ error: 'Not Found' }, { status: 404 }); // 文本响应 return new Response('OK', { status: 200 }); // HTML 响应 return new Response('<h1>Hello</h1>', { headers: { 'Content-Type': 'text/html' }, }); // 重定向 import { redirect } from 'next/navigation'; redirect('/new-url'); // 或者 return Response.redirect(new URL('/new-url', request.url));

4.2 设置响应头

export async function GET() { return Response.json(data, { headers: { 'Cache-Control': 'no-cache', 'X-Custom-Header': 'custom-value', }, }); }

4.3 流式响应

// app/api/stream/route.js export async function GET() { const encoder = new TextEncoder(); const stream = new ReadableStream({ start(controller) { const sendEvent = (data) => { controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)); }; sendEvent({ message: '开始' }); const interval = setInterval(() => { sendEvent({ timestamp: Date.now() }); }, 1000); setTimeout(() => { clearInterval(interval); sendEvent({ message: '结束' }); controller.close(); }, 10000); }, }); return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', }, }); }

5. 中间件和工具

5.1 使用 Cookies

// app/api/auth/route.js import { cookies } from 'next/headers'; export async function POST(request) { const body = await request.json(); // 设置 cookie cookies().set('token', body.token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 24, // 1 天 path: '/', }); return Response.json({ success: true }); } export async function GET() { const token = cookies().get('token'); if (!token) { return Response.json({ error: '未登录' }, { status: 401 }); } return Response.json({ token: token.value }); } export async function DELETE() { cookies().delete('token'); return Response.json({ success: true }); }

5.2 验证用户

// app/api/posts/route.js import { getServerSession } from 'next-auth'; export async function POST(request) { const session = await getServerSession(); if (!session) { return Response.json({ error: '请先登录' }, { status: 401 }); } const body = await request.json(); const post = await db.post.create({ data: { ...body, authorId: session.user.id, }, }); return Response.json(post); }

5.3 请求验证

// app/api/users/route.js import { z } from 'zod'; const userSchema = z.object({ name: z.string().min(2), email: z.string().email(), age: z.number().min(18).optional(), }); export async function POST(request) { try { const body = await request.json(); const validated = userSchema.parse(body); const user = await db.user.create({ data: validated }); return Response.json(user, { status: 201 }); } catch (error) { if (error instanceof z.ZodError) { return Response.json({ error: error.errors }, { status: 400 }); } return Response.json({ error: '服务器错误' }, { status: 500 }); } }

6. 文件上传

6.1 单文件上传

// app/api/upload/route.js import { writeFile } from 'fs/promises'; import path from 'path'; export async function POST(request) { const formData = await request.formData(); const file = formData.get('file'); if (!file) { return Response.json({ error: '没有文件' }, { status: 400 }); } const bytes = await file.arrayBuffer(); const buffer = Buffer.from(bytes); const filename = `${Date.now()}-${file.name}`; const filepath = path.join(process.cwd(), 'public/uploads', filename); await writeFile(filepath, buffer); return Response.json({ filename, url: `/uploads/${filename}` }); }

6.2 多文件上传

// app/api/upload/multiple/route.js export async function POST(request) { const formData = await request.formData(); const files = formData.getAll('files'); const uploadedFiles = []; for (const file of files) { const bytes = await file.arrayBuffer(); const buffer = Buffer.from(bytes); const filename = `${Date.now()}-${file.name}`; const filepath = path.join(process.cwd(), 'public/uploads', filename); await writeFile(filepath, buffer); uploadedFiles.push({ filename, url: `/uploads/${filename}` }); } return Response.json(uploadedFiles); }

7. 完整示例:用户管理系统

// app/api/users/route.js import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { z } from 'zod'; import { db } from '@/lib/db'; const createUserSchema = z.object({ name: z.string().min(2, '姓名至少2个字符'), email: z.string().email('邮箱格式不正确'), role: z.enum(['user', 'admin']).default('user'), }); // GET - 获取用户列表 export async function GET(request) { const session = await getServerSession(); if (!session || session.user.role !== 'admin') { return NextResponse.json({ error: '无权限' }, { status: 403 }); } const { searchParams } = new URL(request.url); const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '10'); const search = searchParams.get('search') || ''; const where = search ? { OR: [ { name: { contains: search } }, { email: { contains: search } }, ], } : {}; const [users, total] = await Promise.all([ db.user.findMany({ where, skip: (page - 1) * limit, take: limit, select: { id: true, name: true, email: true, role: true, createdAt: true }, }), db.user.count({ where }), ]); return NextResponse.json({ data: users, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) }, }); } // POST - 创建用户 export async function POST(request) { const session = await getServerSession(); if (!session || session.user.role !== 'admin') { return NextResponse.json({ error: '无权限' }, { status: 403 }); } try { const body = await request.json(); const validated = createUserSchema.parse(body); const existingUser = await db.user.findUnique({ where: { email: validated.email }, }); if (existingUser) { return NextResponse.json({ error: '邮箱已被使用' }, { status: 400 }); } const user = await db.user.create({ data: validated }); return NextResponse.json(user, { status: 201 }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json({ error: error.errors }, { status: 400 }); } return NextResponse.json({ error: '服务器错误' }, { status: 500 }); } } // app/api/users/[id]/route.js import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { db } from '@/lib/db'; // GET - 获取单个用户 export async function GET(request, { params }) { const { id } = await params; const session = await getServerSession(); if (!session || (session.user.role !== 'admin' && session.user.id !== id)) { return NextResponse.json({ error: '无权限' }, { status: 403 }); } const user = await db.user.findUnique({ where: { id }, select: { id: true, name: true, email: true, role: true, createdAt: true }, }); if (!user) { return NextResponse.json({ error: '用户不存在' }, { status: 404 }); } return NextResponse.json(user); } // PUT - 更新用户 export async function PUT(request, { params }) { const { id } = await params; const session = await getServerSession(); if (!session || (session.user.role !== 'admin' && session.user.id !== id)) { return NextResponse.json({ error: '无权限' }, { status: 403 }); } const body = await request.json(); const user = await db.user.update({ where: { id }, data: body, }); return NextResponse.json(user); } // DELETE - 删除用户 export async function DELETE(request, { params }) { const { id } = await params; const session = await getServerSession(); if (!session || session.user.role !== 'admin') { return NextResponse.json({ error: '无权限' }, { status: 403 }); } await db.user.delete({ where: { id } }); return NextResponse.json({ success: true }); }

8. 总结

核心要点

要点说明
位置app/api/目录下的route.js
HTTP 方法GET、POST、PUT、PATCH、DELETE
动态路由[id][...slug][[...slug]]
响应Response.json()NextResponse

最佳实践

  • 使用 Zod 进行请求验证
  • 实现认证和授权
  • 正确处理错误
  • 设置合适的缓存头
  • 避免在 API 中直接操作 DOM

记忆口诀

API Routes 在 api 目录下,route.js 来处理
HTTP 方法做导出,动态参数用括号
请求响应都简单,验证授权不可少


9. 相关资源

  • Next.js API Routes
  • NextResponse API
  • Zod 验证库

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 14:10:37

使用vim编辑器和shell命令补充

使用vim编辑器和shell命令补充 第一部分&#xff1a;知识点概念理解与实操Vim 编辑器 &#xff08;1&#xff09;概念理解&#xff1a;Vim 是 Linux 下功能强大的文本编辑器&#xff0c;是 Vi 的升级版。它采用全屏模式&#xff0c;具有多种操作模式&#xff08;普通模式、插入…

作者头像 李华
网站建设 2026/7/6 14:06:42

《抽象数据类型:把数据和对数据的操作绑在一起》

前言C 语言是一门面向过程的语言&#xff0c;没有 class 类&#xff0c;但是我们可以利用头文件.h和源文件.c分离的写法&#xff0c;模拟面向对象里的封装思想&#xff0c;也就是抽象数据类型&#xff08;ADT&#xff09;。 所有数据结构统一使用这套规范编写&#xff0c;代码整…

作者头像 李华
网站建设 2026/7/6 14:05:59

BsMax插件:3ds Max用户如何在Blender中无缝延续专业工作流?

BsMax插件&#xff1a;3ds Max用户如何在Blender中无缝延续专业工作流&#xff1f; 【免费下载链接】BsMax BsMax Blender Addon (UI simulator/ Modeling/ Rigg & Animation/ Render Tools and ... 项目地址: https://gitcode.com/gh_mirrors/bs/BsMax 从3ds Max转…

作者头像 李华
网站建设 2026/7/6 13:56:46

RL 的 token 选择之争:高熵词到底重不重要?

一句话总结 有的研究认为 RL 时要重点训高熵 token&#xff08;激发探索&#xff09;&#xff0c;有的则认为要避开它们&#xff08;干扰优化&#xff09;&#xff0c;关键是双方的实验都能涨点。作者提出 “相对惊喜度” 指标来同时刻画 token 的不确定性与梯度影响&#xff0…

作者头像 李华
网站建设 2026/7/6 13:56:49

批量水印自动化:让照片参数自动变成艺术标签

批量水印自动化&#xff1a;让照片参数自动变成艺术标签 【免费下载链接】semi-utils 一个批量添加相机机型和拍摄参数的工具&#xff0c;后续「可能」添加其他功能。 项目地址: https://gitcode.com/gh_mirrors/se/semi-utils 还在为每张照片手动添加拍摄参数而烦恼吗&…

作者头像 李华
网站建设 2026/7/6 13:54:40

阿里:语言世界模型赋能通用智能体

&#x1f4d6;标题&#xff1a;Qwen-AgentWorld: Language World Models for General Agents &#x1f310;来源&#xff1a;arXiv, 2606.24597v1 &#x1f6ce;️文章简介 &#x1f538;研究问题&#xff1a;如何构建基于语言模型的通用环境模拟器&#xff0c;并探索其提升智能…

作者头像 李华