API 路由
Next.js 允许在同一项目中创建 API 接口,无需单独的后端服务。本章将介绍如何使用 Route Handlers 构建 API。
Route Handlers
在 App Router 中,使用 route.ts 文件定义 API 路由。
基本 API
// src/app/api/hello/route.ts
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
message: "Hello from API",
});
}
访问 /api/hello 将返回 JSON 响应。
HTTP 方法
支持所有 HTTP 方法:
// src/app/api/posts/route.ts
import { NextResponse } from "next/server";
// GET /api/posts
export async function GET() {
const posts = await getPosts();
return NextResponse.json(posts);
}
// POST /api/posts
export async function POST(request: Request) {
const body = await request.json();
const post = await createPost(body);
return NextResponse.json(post, { status: 201 });
}
// PUT /api/posts
export async function PUT(request: Request) {
const body = await request.json();
const post = await updatePost(body);
return NextResponse.json(post);
}
// DELETE /api/posts
export async function DELETE(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");
await deletePost(id);
return new NextResponse(null, { status: 204 });
}
请求处理
获取请求体
export async function POST(request: Request) {
// JSON 数据
const json = await request.json();
// 表单数据
const formData = await request.formData();
const name = formData.get("name");
// 文本数据
const text = await request.text();
return NextResponse.json({ received: json });
}
获取 URL 参数
// src/app/api/posts/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const post = await getPost(params.id);
return NextResponse.json(post);
}
获取查询参数
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = searchParams.get("page") || "1";
const limit = searchParams.get("limit") || "10";
const posts = await getPosts({ page, limit });
return NextResponse.json(posts);
}
获取请求头
export async function GET(request: Request) {
const authorization = request.headers.get("authorization");
const userAgent = request.headers.get("user-agent");
return NextResponse.json({ authorization, userAgent });
}
获取 Cookie
import { cookies } from "next/headers";
export async function GET() {
const cookieStore = await cookies();
const token = cookieStore.get("token");
return NextResponse.json({ token: token?.value });
}
响应处理
JSON 响应
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json(
{ message: "Success", data: [] },
{ status: 200 }
);
}
自定义响应头
export async function GET() {
return NextResponse.json(
{ message: "Success" },
{
headers: {
"Cache-Control": "public, max-age=3600",
"X-Custom-Header": "value",
},
}
);
}
重定向
import { redirect } from "next/navigation";
export async function GET() {
redirect("/login");
}
设置 Cookie
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const body = await request.json();
const response = NextResponse.json({ success: true });
response.cookies.set("token", "your-jwt-token", {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 60 * 60 * 24 * 7, // 7 天
});
return response;
}
动态路由
动态段
// src/app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await getUser(params.id);
if (!user) {
return NextResponse.json({ error: "用户不存在" }, { status: 404 });
}
return NextResponse.json(user);
}
捕获所有路由
// src/app/api/docs/[...slug]/route.ts
export async function GET(
request: Request,
{ params }: { params: { slug: string[] } }
) {
const path = params.slug.join("/");
const doc = await getDoc(path);
return NextResponse.json(doc);
}
中间件集成
在 API 路由中使用中间件验证:
// src/app/api/admin/route.ts
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
async function verifyAuth() {
const cookieStore = await cookies();
const token = cookieStore.get("token");
if (!token) return null;
return verifyToken(token.value);
}
export async function GET() {
const user = await verifyAuth();
if (!user) {
return NextResponse.json({ error: "未授权" }, { status: 401 });
}
return NextResponse.json({ user });
}
实战示例
用户注册 API
// src/app/api/auth/register/route.ts
import { NextResponse } from "next/server";
import { hash } from "bcrypt";
import { z } from "zod";
const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
name: z.string().min(2),
});
export async function POST(request: Request) {
try {
const body = await request.json();
// 验证输入
const { email, password, name } = registerSchema.parse(body);
// 检查用户是否存在
const existingUser = await db.user.findUnique({ where: { email } });
if (existingUser) {
return NextResponse.json(
{ error: "邮箱已被注册" },
{ status: 400 }
);
}
// 创建用户
const hashedPassword = await hash(password, 10);
const user = await db.user.create({
data: { email, password: hashedPassword, name },
});
return NextResponse.json(
{ id: user.id, email: user.email, name: user.name },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ error: "注册失败" },
{ status: 500 }
);
}
}
文件上传 API
// src/app/api/upload/route.ts
import { NextResponse } from "next/server";
import { writeFile } from "fs/promises";
import path from "path";
export async function POST(request: Request) {
try {
const formData = await request.formData();
const file = formData.get("file") as File;
if (!file) {
return NextResponse.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 NextResponse.json({
url: `/uploads/${filename}`,
filename,
});
} catch (error) {
return NextResponse.json({ error: "上传失败" }, { status: 500 });
}
}
分页 API
// src/app/api/posts/route.ts
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get("page") || "1");
const limit = parseInt(searchParams.get("limit") || "10");
const skip = (page - 1) * limit;
const [posts, total] = await Promise.all([
db.post.findMany({
skip,
take: limit,
orderBy: { createdAt: "desc" },
}),
db.post.count(),
]);
return NextResponse.json({
data: posts,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
});
}
小结
本章我们学习了:
- Route Handlers 的基本用法
- 各种 HTTP 方法的处理
- 请求参数和请求体的获取
- 响应的构建和 Cookie 操作
- 动态路由 API
- 实战示例
练习
- 创建一个简单的 CRUD API(增删改查)
- 实现一个带验证的用户登录 API
- 创建一个分页查询 API
- 实现一个文件上传 API