路由处理程序
路由处理程序允许你使用 Web 请求 和 响应 API 为给定路由创建自定义请求处理程序。
¥Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.
很高兴知道:路由处理程序仅在
app
目录中可用。它们相当于pages
目录中的 API 路由,这意味着你不需要同时使用 API 路由和路由处理程序。¥Good to know: Route Handlers are only available inside the
app
directory. They are the equivalent of API Routes inside thepages
directory meaning you do not need to use API Routes and Route Handlers together.
惯例
¥Convention
路由处理程序在 app
目录内的 route.js|ts
file 中定义:
¥Route Handlers are defined in a route.js|ts
file inside the app
directory:
export async function GET(request: Request) {}
export async function GET(request) {}
路由处理程序可以嵌套在 app
目录内的任何位置,类似于 page.js
和 layout.js
。但不能有 route.js
文件与 page.js
处于同一航段级别。
¥Route Handlers can be nested anywhere inside the app
directory, similar to page.js
and layout.js
. But there cannot be a route.js
file at the same route segment level as page.js
.
支持的 HTTP 方法
¥Supported HTTP Methods
支持以下 HTTP 方法:GET
、POST
、PUT
、PATCH
、DELETE
、HEAD
和 OPTIONS
。如果调用不受支持的方法,Next.js 将返回 405 Method Not Allowed
响应。
¥The following HTTP methods are supported: GET
, POST
, PUT
, PATCH
, DELETE
, HEAD
, and OPTIONS
. If an unsupported method is called, Next.js will return a 405 Method Not Allowed
response.
扩展 NextRequest
和 NextResponse
API
¥Extended NextRequest
and NextResponse
APIs
除了支持原生 请求 和 响应 API 之外,Next.js 还使用 NextRequest
和 NextResponse
扩展了它们,为高级用例提供方便的辅助程序。
¥In addition to supporting the native Request and Response APIs, Next.js extends them with NextRequest
and NextResponse
to provide convenient helpers for advanced use cases.
行为
¥Behavior
缓存
¥Caching
默认情况下,路由处理程序不缓存。但是,你可以选择缓存 GET
方法。其他支持的 HTTP 方法不会被缓存。要缓存 GET
方法,请在路由处理程序文件中使用 路由配置选项(例如 export const dynamic = 'force-static'
)。
¥Route Handlers are not cached by default. You can, however, opt into caching for GET
methods. Other supported HTTP methods are not cached. To cache a GET
method, use a route config option such as export const dynamic = 'force-static'
in your Route Handler file.
export const dynamic = 'force-static'
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
export const dynamic = 'force-static'
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
很高兴知道:其他支持的 HTTP 方法不会被缓存,即使它们与缓存的
GET
方法放在同一个文件中。¥Good to know: Other supported HTTP methods are not cached, even if they are placed alongside a
GET
method that is cached, in the same file.
特殊路由处理程序
¥Special Route Handlers
特殊路由处理程序(如 sitemap.ts
、opengraph-image.tsx
和 icon.tsx
)和其他 元数据文件 默认保持静态,除非它们使用动态 API 或动态配置选项。
¥Special Route Handlers like sitemap.ts
, opengraph-image.tsx
, and icon.tsx
, and other metadata files remain static by default unless they use Dynamic APIs or dynamic config options.
路由解析
¥Route Resolution
你可以将 route
视为最底层的路由原语。
¥You can consider a route
the lowest level routing primitive.
-
它们不像
page
那样参与布局或客户端导航。¥They do not participate in layouts or client-side navigations like
page
. -
route.js
文件不能与page.js
在同一路径上。¥There cannot be a
route.js
file at the same route aspage.js
.
页面 | 路由 | 结果 |
---|---|---|
app/page.js | app/route.js | 冲突 |
app/page.js | app/api/route.js | 有效的 |
app/[user]/page.js | app/api/route.js | 有效的 |
每个 route.js
或 page.js
文件都会接管该路由的所有 HTTP 动词。
¥Each route.js
or page.js
file takes over all HTTP verbs for that route.
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// ❌ Conflict
// `app/route.ts`
export async function POST(request: Request) {}
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// ❌ Conflict
// `app/route.js`
export async function POST(request) {}
示例
¥Examples
以下示例展示了如何将路由处理程序与其他 Next.js API 和功能结合起来。
¥The following examples show how to combine Route Handlers with other Next.js APIs and features.
重新验证缓存数据
¥Revalidating Cached Data
你可以使用增量静态再生 (ISR) 重新验证缓存数据:
¥You can revalidate cached data using Incremental Static Regeneration (ISR):
export const revalidate = 60
export async function GET() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return Response.json(posts)
}
export const revalidate = 60
export async function GET() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return Response.json(posts)
}
Cookies
你可以从 next/headers
读取或设置 cookies
的 cookie。该服务器函数可以直接在路由处理程序中调用,也可以嵌套在另一个函数中。
¥You can read or set cookies with cookies
from next/headers
. This server function can be called directly in a Route Handler, or nested inside of another function.
或者,你可以使用 Set-Cookie
标头返回新的 Response
。
¥Alternatively, you can return a new Response
using the Set-Cookie
header.
import { cookies } from 'next/headers'
export async function GET(request: Request) {
const cookieStore = await cookies()
const token = cookieStore.get('token')
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token.value}` },
})
}
import { cookies } from 'next/headers'
export async function GET(request) {
const cookieStore = await cookies()
const token = cookieStore.get('token')
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token}` },
})
}
你还可以使用底层 Web API 从请求中读取 cookie (NextRequest
):
¥You can also use the underlying Web APIs to read cookies from the request (NextRequest
):
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token')
}
export async function GET(request) {
const token = request.cookies.get('token')
}
标头
¥Headers
你可以从 next/headers
读取带有 headers
的标头。该服务器函数可以直接在路由处理程序中调用,也可以嵌套在另一个函数中。
¥You can read headers with headers
from next/headers
. This server function can be called directly in a Route Handler, or nested inside of another function.
此 headers
实例是只读的。要设置标头,你需要返回新的 Response
和新的 headers
。
¥This headers
instance is read-only. To set headers, you need to return a new Response
with new headers
.
import { headers } from 'next/headers'
export async function GET(request: Request) {
const headersList = await headers()
const referer = headersList.get('referer')
return new Response('Hello, Next.js!', {
status: 200,
headers: { referer: referer },
})
}
import { headers } from 'next/headers'
export async function GET(request) {
const headersList = await headers()
const referer = headersList.get('referer')
return new Response('Hello, Next.js!', {
status: 200,
headers: { referer: referer },
})
}
你还可以使用底层 Web API 从请求中读取标头 (NextRequest
):
¥You can also use the underlying Web APIs to read headers from the request (NextRequest
):
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
}
export async function GET(request) {
const requestHeaders = new Headers(request.headers)
}
重定向
¥Redirects
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
redirect('https://next.nodejs.cn/')
}
import { redirect } from 'next/navigation'
export async function GET(request) {
redirect('https://next.nodejs.cn/')
}
动态路由片段
¥Dynamic Route Segments
我们建议你先阅读 定义路由 页,然后再继续。
¥We recommend reading the Defining Routes page before continuing.
路由处理程序可以使用 动态片段 从动态数据创建请求处理程序。
¥Route Handlers can use Dynamic Segments to create request handlers from dynamic data.
export async function GET(
request: Request,
{ params }: { params: Promise<{ slug: string }> }
) {
const slug = (await params).slug // 'a', 'b', or 'c'
}
export async function GET(request, { params }) {
const slug = (await params).slug // 'a', 'b', or 'c'
}
路由 | 示例网址 | params |
---|---|---|
app/items/[slug]/route.js | /items/a | Promise<{ slug: 'a' }> |
app/items/[slug]/route.js | /items/b | Promise<{ slug: 'b' }> |
app/items/[slug]/route.js | /items/c | Promise<{ slug: 'c' }> |
URL 查询参数
¥URL Query Parameters
传递给路由处理程序的请求对象是一个 NextRequest
实例,其中有 一些额外的便利方法,包括用于更轻松地处理查询参数。
¥The request object passed to the Route Handler is a NextRequest
instance, which has some additional convenience methods, including for more easily handling query parameters.
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
export function GET(request) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
流式
¥Streaming
流式通常与大型语言模型 (LLM)(例如 OpenAI)结合使用,用于 AI 生成的内容。了解有关 AI SDK 的更多信息。
¥Streaming is commonly used in combination with Large Language Models (LLMs), such as OpenAI, for AI-generated content. Learn more about the AI SDK.
import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4-turbo'),
messages,
})
return new StreamingTextResponse(result.toAIStream())
}
import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4-turbo'),
messages,
})
return new StreamingTextResponse(result.toAIStream())
}
这些抽象使用 Web API 来创建流。你还可以直接使用底层 Web API。
¥These abstractions use the Web APIs to create a stream. You can also use the underlying Web APIs directly.
// https://web.nodejs.cn/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time: number) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
// https://web.nodejs.cn/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
请求正文
¥Request Body
你可以使用标准 Web API 方法读取 Request
正文:
¥You can read the Request
body using the standard Web API methods:
export async function POST(request: Request) {
const res = await request.json()
return Response.json({ res })
}
export async function POST(request) {
const res = await request.json()
return Response.json({ res })
}
请求主体表单数据
¥Request Body FormData
你可以使用 request.formData()
函数读取 FormData
:
¥You can read the FormData
using the request.formData()
function:
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
export async function POST(request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
由于 formData
数据都是字符串,因此你可能希望使用 zod-form-data
来验证请求并以你喜欢的格式检索数据(例如 number
)。
¥Since formData
data are all strings, you may want to use zod-form-data
to validate the request and retrieve data in the format you prefer (e.g. number
).
CORS
你可以使用标准 Web API 方法为特定路由处理程序设置 CORS 标头:
¥You can set CORS headers for a specific Route Handler using the standard Web API methods:
export async function GET(request: Request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
export async function GET(request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
很高兴知道:
¥Good to know:
要将 CORS 标头添加到多个路由处理程序,你可以使用 中间件 或
next.config.js
file。¥To add CORS headers to multiple Route Handlers, you can use Middleware or the
next.config.js
file.或者,请参阅我们的 跨域资源共享示例 包。
¥Alternatively, see our CORS example package.
Webhook
你可以使用路由处理程序从第三方服务接收 webhook:
¥You can use a Route Handler to receive webhooks from third-party services:
export async function POST(request: Request) {
try {
const text = await request.text()
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
})
}
return new Response('Success!', {
status: 200,
})
}
export async function POST(request) {
try {
const text = await request.text()
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
})
}
return new Response('Success!', {
status: 200,
})
}
值得注意的是,与页面路由的 API 路由不同,你不需要使用 bodyParser
来使用任何其他配置。
¥Notably, unlike API Routes with the Pages Router, you do not need to use bodyParser
to use any additional configuration.
非 UI 响应
¥Non-UI Responses
你可以使用路由处理程序返回非 UI 内容。请注意,sitemap.xml
、robots.txt
、app icons
和 打开图形图片 都有内置支持。
¥You can use Route Handlers to return non-UI content. Note that sitemap.xml
, robots.txt
, app icons
, and open graph images all have built-in support.
export async function GET() {
return new Response(
`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Next.js Documentation</title>
<link>https://next.nodejs.cn/docs</link>
<description>The React Framework for the Web</description>
</channel>
</rss>`,
{
headers: {
'Content-Type': 'text/xml',
},
}
)
}
export async function GET() {
return new Response(`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Next.js Documentation</title>
<link>https://next.nodejs.cn/docs</link>
<description>The React Framework for the Web</description>
</channel>
</rss>`)
}
片段配置选项
¥Segment Config Options
路由处理程序使用与页面和布局相同的 路由段配置。
¥Route Handlers use the same route segment configuration as pages and layouts.
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
有关详细信息,请参阅 API 参考。
¥See the API reference for more details.