API 路由
须知:如果您使用应用路由 (App Router),可以使用 服务端组件 (Server Components) 或 路由处理器 (Route Handlers) 替代 API 路由。
API 路由提供了在 Next.js 中构建公共 API 的解决方案。
pages/api 目录下的任何文件都会被映射到 /api/* 路径,并被视为 API 端点而非页面。这些文件仅在生产服务端打包,不会增加客户端包体积。
例如,以下 API 路由会返回状态码为 200 的 JSON 响应:
import type { NextApiRequest, NextApiResponse } from 'next'
type ResponseData = {
message: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
res.status(200).json({ message: 'Hello from Next.js!' })
}export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' })
}须知:
- API 路由默认不指定 CORS 头信息,意味着默认情况下它们仅支持同源请求。您可以通过使用 CORS 请求辅助工具 包装请求处理函数来自定义此行为。
- API 路由不能与静态导出 (static exports) 一起使用。但应用路由中的路由处理器 (Route Handlers) 可以支持。
- API 路由会受到
next.config.js中的pageExtensions配置 影响。
参数
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// ...
}req: http.IncomingMessage 实例,附加了预置中间件res: http.ServerResponse 实例,附加了辅助函数
HTTP 方法
要在 API 路由中处理不同的 HTTP 方法,可以在请求处理函数中使用 req.method,如下所示:
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'POST') {
// 处理 POST 请求
} else {
// 处理其他 HTTP 方法
}
}export default function handler(req, res) {
if (req.method === 'POST') {
// 处理 POST 请求
} else {
// 处理其他 HTTP 方法
}
}请求辅助工具
API 路由提供了内置的请求辅助工具来解析传入请求 (req):
req.cookies- 包含请求发送的 cookie 对象。默认为{}req.query- 包含查询字符串的对象。默认为{}req.body- 包含根据content-type解析的请求体对象,若无请求体则为null
自定义配置
每个 API 路由都可以导出一个 config 对象来修改默认配置,默认配置如下:
export const config = {
api: {
bodyParser: {
sizeLimit: '1mb',
},
},
// 指定此函数允许执行的最长持续时间(秒)
maxDuration: 5,
}bodyParser 默认启用。如果您需要以 Stream 形式或使用 raw-body 处理请求体,可以将其设为 false。
禁用自动 bodyParsing 的一个用例是验证 webhook 请求的原始内容,例如来自 GitHub 的 webhook。
export const config = {
api: {
bodyParser: false,
},
}bodyParser.sizeLimit 是解析后请求体允许的最大大小,支持 bytes 的任何格式,例如:
export const config = {
api: {
bodyParser: {
sizeLimit: '500kb',
},
},
}externalResolver 是一个显式标志,用于告知服务器此路由由外部解析器(如 express 或 connect)处理。启用此选项会禁用未解析请求的警告。
export const config = {
api: {
externalResolver: true,
},
}responseLimit 默认启用,当 API 路由响应体超过 4MB 时会发出警告。
如果您不是在无服务器环境中使用 Next.js,并且了解不使用 CDN 或专用媒体托管服务的性能影响,可以将此限制设为 false。
export const config = {
api: {
responseLimit: false,
},
}responseLimit 也可以设置为字节数或 bytes 支持的任何字符串格式,例如 1000、'500kb' 或 '3mb'。
此值将成为触发警告前的最大响应大小。默认值为 4MB(如上所述)。
export const config = {
api: {
responseLimit: '8mb',
},
}响应辅助工具
服务端响应对象(通常简写为 res)包含一组类似 Express.js 的辅助方法,用于提升开发体验并加速 API 端点的创建。
包含的辅助方法有:
res.status(code)- 设置状态码的函数。code必须是有效的 HTTP 状态码res.json(body)- 发送 JSON 响应。body必须是可序列化对象res.send(body)- 发送 HTTP 响应。body可以是string、object或Bufferres.redirect([status,] path)- 重定向到指定路径或 URL。status必须是有效的 HTTP 状态码。若未指定,status默认为 "307" "临时重定向"。res.revalidate(urlPath)- 按需重新验证页面 使用getStaticProps。urlPath必须为string。
设置响应状态码
向客户端发送响应时,可以设置响应的状态码。
以下示例将响应状态码设置为 200(OK),并以 JSON 格式返回包含 message 属性的响应,其值为 Hello from Next.js!:
import type { NextApiRequest, NextApiResponse } from 'next'
type ResponseData = {
message: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
res.status(200).json({ message: 'Hello from Next.js!' })
}export default function handler(req, res) {
res.status(200).json({ message: 'Hello from Next.js!' })
}发送 JSON 响应
向客户端发送响应时,可以发送 JSON 响应,这必须是一个可序列化对象。 在实际应用中,您可能希望根据请求端点的结果让客户端了解请求状态。
以下示例发送状态码为 200(OK)的 JSON 响应,包含异步操作的结果。代码使用 try catch 块处理可能发生的错误,捕获适当的状态码和错误消息并返回给客户端:
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const result = await someAsyncOperation()
res.status(200).json({ result })
} catch (err) {
res.status(500).json({ error: 'failed to load data' })
}
}export default async function handler(req, res) {
try {
const result = await someAsyncOperation()
res.status(200).json({ result })
} catch (err) {
res.status(500).json({ error: 'failed to load data' })
}
}发送 HTTP 响应
发送 HTTP 响应的方式与发送 JSON 响应类似。唯一的区别是响应体可以是 string、object 或 Buffer。
以下示例发送状态码为 200(OK)的 HTTP 响应,包含异步操作的结果:
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const result = await someAsyncOperation()
res.status(200).send({ result })
} catch (err) {
res.status(500).send({ error: 'failed to fetch data' })
}
}export default async function handler(req, res) {
try {
const result = await someAsyncOperation()
res.status(200).send({ result })
} catch (err) {
res.status(500).send({ error: 'failed to fetch data' })
}
}重定向到指定路径或 URL
以表单为例,您可能希望在客户端提交表单后将其重定向到指定路径或 URL。
以下示例在表单成功提交后将客户端重定向到 / 路径:
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { name, message } = req.body
try {
await handleFormInputAsync({ name, message })
res.redirect(307, '/')
} catch (err) {
res.status(500).send({ error: 'Failed to fetch data' })
}
}export default async function handler(req, res) {
const { name, message } = req.body
try {
await handleFormInputAsync({ name, message })
res.redirect(307, '/')
} catch (err) {
res.status(500).send({ error: 'failed to fetch data' })
}
}添加 TypeScript 类型
您可以通过从 next 导入 NextApiRequest 和 NextApiResponse 类型来增强 API 路由的类型安全性,此外还可以为响应数据添加类型:
import type { NextApiRequest, NextApiResponse } from 'next'
type ResponseData = {
message: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>
) {
res.status(200).json({ message: 'Hello from Next.js!' })
}须知:
NextApiRequest的 body 是any类型,因为客户端可能包含任何有效载荷。在使用前应验证 body 的类型/结构。
动态 API 路由
API 路由支持动态路由,并遵循与 pages/ 相同的文件命名规则。
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { pid } = req.query
res.end(`Post: ${pid}`)
}export default function handler(req, res) {
const { pid } = req.query
res.end(`Post: ${pid}`)
}现在,向 /api/post/abc 发送请求将返回文本:Post: abc。
全捕获 API 路由
通过在方括号内添加三个点(...)可以扩展 API 路由以捕获所有路径。例如:
pages/api/post/[...slug].js会匹配/api/post/a,也会匹配/api/post/a/b、/api/post/a/b/c等路径。
须知:您可以使用
slug以外的名称,例如:[...param]
匹配的参数会作为查询参数(示例中的 slug)传递给页面,且始终是一个数组。因此路径 /api/post/a 会有如下 query 对象:
{ "slug": ["a"] }而对于 /api/post/a/b 或其他匹配路径,新参数会被添加到数组中:
{ "slug": ["a", "b"] }例如:
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { slug } = req.query
res.end(`Post: ${slug.join(', ')}`)
}export default function handler(req, res) {
const { slug } = req.query
res.end(`Post: ${slug.join(', ')}`)
}现在,向 /api/post/a/b/c 发送请求将返回文本:Post: a, b, c。
可选全捕获 API 路由
通过将参数放在双方括号中([[...slug]]),可以使全捕获路由变为可选。
例如,pages/api/post/[[...slug]].js 会匹配 /api/post、/api/post/a、/api/post/a/b 等路径。
全捕获路由与可选全捕获路由的主要区别在于,可选路由还会匹配不带参数的路径(示例中的 /api/post)。
query 对象如下:
{ } // GET `/api/post` (空对象)
{ "slug": ["a"] } // `GET /api/post/a` (单元素数组)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (多元素数组)注意事项
- 预定义的 API 路由优先于动态 API 路由,动态 API 路由优先于全捕获 API 路由。请看以下示例:
pages/api/post/create.js- 会匹配/api/post/createpages/api/post/[pid].js- 会匹配/api/post/1、/api/post/abc等,但不匹配/api/post/createpages/api/post/[...slug].js- 会匹配/api/post/1/2、/api/post/a/b/c等,但不匹配/api/post/create、/api/post/abc
Edge API 路由
如果您希望在 Edge Runtime 中使用 API 路由,我们推荐逐步采用应用路由并使用路由处理器 (Route Handlers) 替代。
路由处理器的函数签名是同构的,意味着您可以对 Edge 和 Node.js 运行时使用相同的函数。