12 KiB
API请求规范与最佳实践
目录
基本规范
响应格式
所有API响应应遵循统一的格式:
// 成功响应
{
success: true,
data?: any, // 通用数据字段
[key: string]: any, // 或使用特定业务字段名(如users, products等)
message?: string // 可选,成功消息
}
// 错误响应
{
success: false,
error: string, // 错误消息
code?: string // 可选,错误代码
}
示例:通用数据字段
return NextResponse.json({
success: true,
data: rows
});
示例:特定业务字段
return NextResponse.json({
success: true,
users: rows // 使用更具描述性的字段名
});
错误处理
始终使用try-catch处理API操作,并返回适当的错误信息:
try {
// API操作
} catch (error) {
console.error('操作描述失败:', error);
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : '未知错误'
},
{ status: 500 }
);
}
参数验证
在执行操作前验证输入参数:
// 获取参数
const { id } = params;
// 验证参数
if (!id || isNaN(Number(id))) {
return NextResponse.json(
{ success: false, error: '无效的ID参数' },
{ status: 400 }
);
}
// 继续操作
安全性考虑
- 使用参数化查询防止SQL注入
- 不要在响应中返回敏感信息(如密码、令牌)
- 对数据库操作使用最小权限原则
- 验证用户权限和身份认证信息
// 良好实践 - 使用参数化查询
const [user] = await req.db.query(
'SELECT id, username FROM users WHERE id = ?',
[userId]
);
// 不良实践 - 容易遭受SQL注入
const [user] = await req.db.query(
`SELECT * FROM users WHERE id = ${userId}`
);
API路由实现
基础API路由
使用数据库连接中间件的API路由基本实现:
// API路由文件(app/api/your-route/route.ts)
import { NextResponse } from 'next/server';
import { connectSystemDB, RequestWithDB } from '@/lib/db';
async function handler(req: RequestWithDB) {
try {
// 使用req.db访问数据库连接
const [rows] = await req.db.query('SELECT * FROM your_table');
return NextResponse.json({
success: true,
data: rows
});
} catch (error) {
console.error('操作失败:', error);
return NextResponse.json(
{ success: false, error: '数据库操作失败' },
{ status: 500 }
);
}
}
// 导出使用中间件包装的处理函数
export const GET = connectSystemDB(handler);
必要的导入说明
在Next.js的API路由中,通常需要以下两个核心导入:
import { NextRequest, NextResponse } from 'next/server';
-
NextRequest: 扩展了原生Request对象,提供了额外的便利方法和属性,如:
nextUrl
: 获取解析后的URL对象,可访问pathname
、searchParams
等cookies
: 访问和操作请求的Cookieheaders
: 获取请求头json()
: 解析JSON请求体
-
NextResponse: 用于创建和返回响应,提供了多种便利方法:
NextResponse.json()
: 创建JSON响应,自动设置Content-TypeNextResponse.redirect()
: 创建重定向响应NextResponse.rewrite()
: 创建重写响应NextResponse.next()
: 继续请求处理链
这两个对象是构建API路由的基础,几乎在所有API路由实现中都需要使用。
动态路由实现
在Next.js的动态路由中,必须正确处理动态参数。在13.4以后的版本中,动态路由参数需要使用await
来获取:
// 正确的动态路由实现
// API路由文件(app/api/your-route/[param]/route.ts)
import { NextRequest, NextResponse } from 'next/server';
import { connectSystemDB, RequestWithDB } from '@/lib/db';
export const GET = async (
req: NextRequest,
context: { params: { param: string } }
) => {
try {
// 正确获取动态参数 - 使用await
const params = await context.params;
const param = params.param;
// 验证参数
if (!param) {
return NextResponse.json(
{ success: false, error: '无效的参数' },
{ status: 400 }
);
}
// 处理函数
const handler = async (dbReq: RequestWithDB) => {
// 数据库操作
const [rows] = await dbReq.db.query(
'SELECT * FROM table WHERE id = ?',
[param]
);
return NextResponse.json({
success: true,
data: rows
});
};
// 执行处理函数
return await connectSystemDB(handler)(req);
} catch (error) {
console.error('操作失败:', error);
return NextResponse.json(
{ success: false, error: '操作失败' },
{ status: 500 }
);
}
};
注意事项:
- 使用箭头函数定义路由处理器:
export const GET = async (...) => {...}
- 正确获取动态参数:
const params = await context.params;
- 使用ES6解构后再使用参数:
const param = params.param;
- 不要直接解构:
const { param } = context.params;
- 这会导致错误
路由处理函数参数说明
在Next.js App Router中,路由处理函数(如GET、POST、PUT、DELETE等)需要接收两个参数:
- 第一个参数:
request: NextRequest
- 包含请求信息的对象,包括请求头、URL、搜索参数等 - 第二个参数:
context: { params: { [key: string]: string } }
- 包含动态路由参数的上下文对象
对于动态路由如/api/users/[id]
,路由处理函数会接收如下参数:
export async function GET(
request: NextRequest,
context: { params: { id: string } }
) {
const userId = context.params.id;
// 使用userId处理请求...
}
这种双参数模式对于获取动态路由参数非常有用,但在Next.js 15.3+版本中已被弃用,转而使用单参数模式,从URL中提取参数(见下文)。
Next.js 15.3+路由处理器
在Next.js 15.3+版本中,路由处理器的类型定义发生了变化。为避免类型错误,应使用以下方式实现API路由:
单参数路由处理器模式
对于动态路由,应使用单参数函数并从URL中提取路径参数,避免使用带context的双参数函数:
/**
* 符合Next.js 15.3+的路由处理器签名
*/
export async function GET(request: NextRequest) {
try {
// 从URL路径中提取动态参数
const pathname = request.nextUrl.pathname;
const parts = pathname.split('/');
const paramValue = parts[3]; // 例如: /api/path/[param]
// 验证参数
if (!paramValue) {
return NextResponse.json(
{ success: false, error: '参数不能为空' },
{ status: 400 }
);
}
// 处理逻辑
// ...
} catch (error) {
// 错误处理
}
}
路由处理器函数声明方式
推荐使用函数声明而非箭头函数:
// 推荐 ✅
export async function GET(request: NextRequest) {
// 处理逻辑
}
// 不推荐 ❌
export const GET = async (request: NextRequest) => {
// 处理逻辑
};
修复类型错误示例
如果构建时出现以下类型错误:
Type '{ __tag__: "GET"; __param_position__: "second"; __param_type__: { params: { paramName: string; }; }; }'
does not satisfy the constraint 'ParamCheck<RouteContext>'.
应将路由函数从:
// 可能导致类型错误的实现 ❌
export async function GET(
request: NextRequest,
{ params }: { params: { paramName: string } }
) {
const value = params.paramName;
// ...
}
修改为:
// 修复后的实现 ✅
export async function GET(request: NextRequest) {
// 从URL中提取参数
const pathname = request.nextUrl.pathname;
const paramName = pathname.split('/')[3]; // 适当调整索引位置
// ...继续处理
}
最佳实践
- 使用标准格式:始终使用统一的响应格式
- 错误处理:所有API路由都应包含try-catch块
- 参数验证:在处理前验证所有输入参数
- 日志记录:记录关键操作和错误信息
- 权限验证:确保用户有权限执行请求的操作
- 状态码使用:使用正确的HTTP状态码
- 200: 成功
- 201: 创建成功
- 400: 请求错误/参数无效
- 401: 未授权
- 403: 禁止访问
- 404: 资源不存在
- 500: 服务器内部错误
- 客户端组件中使用useSearchParams:始终将使用useSearchParams的组件包裹在Suspense边界中,避免构建警告
常见问题
问:如何处理文件上传?
答:在Next.js的App Router中,使用formData
来处理文件上传:
export const POST = async (req: NextRequest) => {
try {
const formData = await req.formData();
const file = formData.get('file') as File;
// 处理文件上传
// ...
return NextResponse.json({ success: true });
} catch (error) {
return NextResponse.json(
{ success: false, error: '文件上传失败' },
{ status: 500 }
);
}
};
问:如何实现分页API?
答:使用查询参数实现分页:
export const GET = async (req: NextRequest) => {
try {
const { searchParams } = new URL(req.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
const offset = (page - 1) * limit;
// 查询数据
// ...
return NextResponse.json({
success: true,
data: rows,
pagination: {
page,
limit,
total: totalCount
}
});
} catch (error) {
// 错误处理
}
};
问:如何处理动态路由中的错误?
答:确保正确使用await获取参数,并使用try-catch处理可能的错误:
try {
const params = await context.params;
// 使用params...
} catch (error) {
console.error('参数获取失败:', error);
return NextResponse.json(
{ success: false, error: '参数处理错误' },
{ status: 400 }
);
}
问:如何修复Next.js 15.3+中的路由处理器类型错误?
答:使用单参数路由处理器,从请求URL中提取路径参数:
export async function GET(request: NextRequest) {
try {
// 从URL路径中提取动态参数
const pathname = request.nextUrl.pathname;
const paramValue = pathname.split('/')[3]; // 根据路径结构调整索引
// 继续处理...
} catch (error) {
// 错误处理
}
}
问:如何处理useSearchParams导致的构建警告?
答:将使用useSearchParams的组件包裹在Suspense边界中:
// 正确处理useSearchParams的方式
'use client';
import { Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
// 创建一个独立的组件处理搜索参数
function SearchParamsHandler({ onParamsChange }) {
const searchParams = useSearchParams();
// 使用searchParams的逻辑
// ...
return null;
}
export default function Page() {
// 页面主要内容
return (
<div>
{/* 包裹在Suspense中 */}
<Suspense fallback={null}>
<SearchParamsHandler onParamsChange={handleParamsChange} />
</Suspense>
{/* 页面其他内容 */}
</div>
);
}
文档由阿瑞创建和维护。如有问题,请联系系统管理员。