Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

目录

  1. API 路由简介
  2. 创建基本 API 路由
  3. 处理不同 HTTP 方法
  4. 动态 API 路由
  5. 请求和响应处理
  6. 中间件使用
  7. 数据库集成
  8. 错误处理和验证

1. API 路由简介

Next.js 不仅是一个前端框架,它还提供了强大的后端能力。通过 API 路由功能,你可以在同一个项目中创建后端 API 接口,无需单独维护后端服务。

API 路由的优势:

  • 统一开发体验: 前后端代码在同一项目中管理
  • 简化部署: 前后端一起部署,减少运维复杂度
  • 快速原型开发: 快速构建全栈应用
  • 无缝集成: 前端可以直接调用同源 API

2. 创建基本 API 路由

在 Next.js 中,API 路由文件放置在 src/app/api/ 目录下。每个路由文件都会自动成为对应的 API 端点。

创建第一个 API 路由

创建 src/app/api/hello/route.ts

1
2
3
4
5
6
// src/app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
return NextResponse.json({ message: 'Hello, Next.js!' });
}

访问 http://localhost:3000/api/hello 就可以看到返回的 JSON 数据。

返回不同类型的响应

1
2
3
4
5
6
7
8
9
10
11
12
// src/app/api/text/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
// 返回纯文本
return new NextResponse('Hello, World!', {
status: 200,
headers: {
'Content-Type': 'text/plain',
},
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/app/api/html/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
// 返回 HTML
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Next.js API</title>
</head>
<body>
<h1>Hello from Next.js API Route!</h1>
</body>
</html>
`;

return new NextResponse(html, {
status: 200,
headers: {
'Content-Type': 'text/html',
},
});
}

3. 处理不同 HTTP 方法

API 路由可以处理各种 HTTP 方法,如 GET、POST、PUT、DELETE 等。

多方法处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// src/app/api/users/route.ts
import { NextResponse } from 'next/server';

// 处理 GET 请求
export async function GET() {
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
];

return NextResponse.json(users);
}

// 处理 POST 请求
export async function POST(request: Request) {
const userData = await request.json();

// 这里应该是实际的数据库操作
const newUser = {
id: Date.now(),
...userData
};

return NextResponse.json(newUser, { status: 201 });
}

// 处理 PUT 请求
export async function PUT(request: Request) {
const userData = await request.json();

// 更新用户逻辑
return NextResponse.json({
message: '用户更新成功',
user: userData
});
}

// 处理 DELETE 请求
export async function DELETE() {
// 删除用户逻辑
return NextResponse.json({ message: '用户删除成功' });
}

测试 API 路由

可以使用 curl 命令测试 API:

1
2
3
4
5
6
7
# GET 请求
curl http://localhost:3000/api/users

# POST 请求
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"王五","email":"wangwu@example.com"}'

4. 动态 API 路由

Next.js 支持动态 API 路由,类似于页面路由中的动态路由。

基本动态路由

创建 src/app/api/users/[id]/route.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// src/app/api/users/[id]/route.ts
import { NextResponse } from 'next/server';

// 模拟数据库
const users = [
{ id: '1', name: '张三', email: 'zhangsan@example.com' },
{ id: '2', name: '李四', email: 'lisi@example.com' },
];

export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const { id } = params;
const user = users.find(u => u.id === id);

if (!user) {
return NextResponse.json(
{ error: '用户未找到' },
{ status: 404 }
);
}

return NextResponse.json(user);
}

export async function PUT(
request: Request,
{ params }: { params: { id: string } }
) {
const { id } = params;
const userData = await request.json();

// 更新用户逻辑
const userIndex = users.findIndex(u => u.id === id);
if (userIndex === -1) {
return NextResponse.json(
{ error: '用户未找到' },
{ status: 404 }
);
}

users[userIndex] = { ...users[userIndex], ...userData };

return NextResponse.json(users[userIndex]);
}

export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
const { id } = params;

const userIndex = users.findIndex(u => u.id === id);
if (userIndex === -1) {
return NextResponse.json(
{ error: '用户未找到' },
{ status: 404 }
);
}

users.splice(userIndex, 1);

return NextResponse.json({ message: '用户删除成功' });
}

复杂动态路由

创建 src/app/api/posts/[userId]/[postId]/route.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/app/api/posts/[userId]/[postId]/route.ts
import { NextResponse } from 'next/server';

export async function GET(
request: Request,
{ params }: { params: { userId: string; postId: string } }
) {
const { userId, postId } = params;

// 模拟获取特定用户的文章
const post = {
id: postId,
userId: userId,
title: '文章标题',
content: '文章内容...',
createdAt: new Date().toISOString()
};

return NextResponse.json(post);
}

5. 请求和响应处理

解析请求数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/app/api/form/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
// 解析 JSON 数据
const jsonData = await request.json();
console.log('JSON 数据:', jsonData);

// 解析 FormData
const formData = await request.formData();
const formObject: Record<string, string> = {};
formData.forEach((value, key) => {
formObject[key] = value.toString();
});
console.log('表单数据:', formObject);

// 获取查询参数
const { searchParams } = new URL(request.url);
const name = searchParams.get('name');
console.log('查询参数 name:', name);

return NextResponse.json({
message: '数据接收成功',
jsonData,
formObject,
queryParams: { name }
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/app/api/auth/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
const { username, password } = await request.json();

// 验证用户凭据(这里简化处理)
if (username === 'admin' && password === 'password') {
const response = NextResponse.json({
message: '登录成功',
user: { id: 1, username: 'admin' }
});

// 设置 Cookie
response.cookies.set('auth-token', 'your-jwt-token', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24, // 24 hours
path: '/',
});

return response;
}

return NextResponse.json(
{ error: '用户名或密码错误' },
{ status: 401 }
);
}

流式响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/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(resolve => setTimeout(resolve, 1000));

controller.enqueue(encoder.encode('第二块数据\n'));

await new Promise(resolve => setTimeout(resolve, 1000));

controller.enqueue(encoder.encode('第三块数据\n'));

controller.close();
},
});

return new Response(stream, {
headers: {
'Content-Type': 'text/plain',
'Transfer-Encoding': 'chunked',
},
});
}

6. 中间件使用

Next.js 中间件可以对请求进行拦截和处理。

创建中间件

创建 src/middleware.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// 中间件函数
export function middleware(request: NextRequest) {
// 获取请求路径
const path = request.nextUrl.pathname;

// 获取 Cookie
const token = request.cookies.get('auth-token');

// 检查是否需要认证的路径
const protectedPaths = ['/api/admin', '/dashboard'];
const isProtectedPath = protectedPaths.some(p => path.startsWith(p));

// 如果是受保护路径但没有认证令牌,则重定向到登录页
if (isProtectedPath && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}

// 添加自定义响应头
const response = NextResponse.next();
response.headers.set('X-Custom-Header', 'Next.js Middleware');

return response;
}

// 配置中间件匹配的路径
export const config = {
matcher: [
/*
* 匹配所有请求路径除了那些以特定前缀开头的:
* - api (API 路由)
* - _next/static (静态文件)
* - _next/image (图像优化文件)
* - favicon.ico (favicon 文件)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};

高级中间件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;

// 日志记录
console.log(`[${new Date().toISOString()}] ${request.method} ${pathname}`);

// 速率限制(简化版)
const ip = request.ip ?? '127.0.0.1';
const rateLimitKey = `rate-limit:${ip}`;

// 这里应该使用 Redis 等存储实际的速率限制计数
// 简化示例中我们只是演示逻辑

// CORS 处理
if (request.method === 'OPTIONS') {
const response = new NextResponse(null, { status: 204 });
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}

// 继续处理请求
const response = NextResponse.next();

// 添加 CORS 头
response.headers.set('Access-Control-Allow-Origin', '*');

return response;
}

7. 数据库集成

Next.js 可以与各种数据库集成,以下是几个常见数据库的示例。

MongoDB 集成

首先安装依赖:

1
npm install mongodb

创建数据库连接模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// src/lib/mongodb.ts
import { MongoClient } from 'mongodb';

const uri = process.env.MONGODB_URI!;
const options = {};

let client: MongoClient;
let clientPromise: Promise<MongoClient>;

if (!process.env.MONGODB_URI) {
throw new Error('请设置 MONGODB_URI 环境变量');
}

if (process.env.NODE_ENV === 'development') {
// 在开发模式下,使用全局变量避免重复连接
let globalWithMongo = global as typeof globalThis & {
_mongoClientPromise?: Promise<MongoClient>;
};

if (!globalWithMongo._mongoClientPromise) {
client = new MongoClient(uri, options);
globalWithMongo._mongoClientPromise = client.connect();
}
clientPromise = globalWithMongo._mongoClientPromise;
} else {
// 在生产模式下,每次都创建新连接
client = new MongoClient(uri, options);
clientPromise = client.connect();
}

export default clientPromise;

在 API 路由中使用 MongoDB:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// src/app/api/users/route.ts
import { NextResponse } from 'next/server';
import clientPromise from '@/lib/mongodb';

export async function GET() {
try {
const client = await clientPromise;
const db = client.db("myDatabase");
const users = await db.collection("users").find({}).toArray();

return NextResponse.json(users);
} catch (error) {
return NextResponse.json(
{ error: '数据库查询失败' },
{ status: 500 }
);
}
}

export async function POST(request: Request) {
try {
const client = await clientPromise;
const db = client.db("myDatabase");
const userData = await request.json();

const result = await db.collection("users").insertOne(userData);

return NextResponse.json(
{ message: '用户创建成功', id: result.insertedId },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ error: '用户创建失败' },
{ status: 500 }
);
}
}

PostgreSQL 集成

安装依赖:

1
npm install pg

创建数据库连接:

1
2
3
4
5
6
7
8
// src/lib/postgres.ts
import { Pool } from 'pg';

const pool = new Pool({
connectionString: process.env.POSTGRES_URL,
});

export default pool;

在 API 路由中使用 PostgreSQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// src/app/api/products/route.ts
import { NextResponse } from 'next/server';
import pool from '@/lib/postgres';

export async function GET() {
try {
const result = await pool.query('SELECT * FROM products');
return NextResponse.json(result.rows);
} catch (error) {
return NextResponse.json(
{ error: '数据库查询失败' },
{ status: 500 }
);
}
}

export async function POST(request: Request) {
try {
const { name, price } = await request.json();

const result = await pool.query(
'INSERT INTO products(name, price) VALUES($1, $2) RETURNING *',
[name, price]
);

return NextResponse.json(result.rows[0], { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: '产品创建失败' },
{ status: 500 }
);
}
}

8. 错误处理和验证

良好的错误处理和数据验证是构建健壮 API 的关键。

错误处理中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// src/lib/error-handler.ts
import { NextResponse } from 'next/server';

export function errorHandler(error: any) {
console.error('API 错误:', error);

// 根据错误类型返回不同的响应
if (error.name === 'ValidationError') {
return NextResponse.json(
{ error: '数据验证失败', details: error.message },
{ status: 400 }
);
}

if (error.name === 'UnauthorizedError') {
return NextResponse.json(
{ error: '未授权访问' },
{ status: 401 }
);
}

if (error.code === 'P2025') {
// Prisma 错误:记录未找到
return NextResponse.json(
{ error: '请求的资源不存在' },
{ status: 404 }
);
}

// 默认服务器错误
return NextResponse.json(
{ error: '服务器内部错误' },
{ status: 500 }
);
}

数据验证

使用 Zod 进行数据验证:

1
npm install zod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/lib/validation.ts
import { z } from 'zod';

// 用户数据验证模式
export const userSchema = z.object({
name: z.string().min(1, '姓名不能为空').max(50, '姓名不能超过50个字符'),
email: z.string().email('请输入有效的邮箱地址'),
age: z.number().min(0).max(150).optional(),
});

// 产品数据验证模式
export const productSchema = z.object({
name: z.string().min(1, '产品名称不能为空').max(100),
price: z.number().positive('价格必须大于0'),
description: z.string().max(1000).optional(),
});

在 API 路由中使用验证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// src/app/api/users/route.ts
import { NextResponse } from 'next/server';
import { userSchema } from '@/lib/validation';
import { errorHandler } from '@/lib/error-handler';

export async function POST(request: Request) {
try {
const body = await request.json();

// 验证数据
const validatedData = userSchema.parse(body);

// 处理有效数据
// 这里应该是实际的数据库操作
const newUser = {
id: Date.now().toString(),
...validatedData
};

return NextResponse.json(newUser, { status: 201 });
} catch (error: any) {
// 处理验证错误
if (error.name === 'ZodError') {
return NextResponse.json(
{
error: '数据验证失败',
details: error.errors
},
{ status: 400 }
);
}

// 处理其他错误
return errorHandler(error);
}
}

自定义错误类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/lib/errors.ts
export class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = 'ValidationError';
}
}

export class UnauthorizedError extends Error {
constructor(message: string) {
super(message);
this.name = 'UnauthorizedError';
}
}

export class NotFoundError extends Error {
constructor(message: string) {
super(message);
this.name = 'NotFoundError';
}
}

总结

通过本教程,你已经学会了:

  1. Next.js API 路由的基本概念和使用方法
  2. 如何处理不同的 HTTP 方法
  3. 动态 API 路由的实现
  4. 请求和响应的高级处理技巧
  5. 中间件的使用
  6. 数据库集成的方法
  7. 错误处理和数据验证的最佳实践

在下一节教程中,我们将探讨 Next.js 的性能优化、SEO 优化和部署策略等高级话题。

评论