Skip to main content

路由处理程序

路由处理程序允许你使用 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 the pages 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.jslayout.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 方法GETPOSTPUTPATCHDELETEHEADOPTIONS。如果调用不受支持的方法,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.

扩展 NextRequestNextResponse API

¥Extended NextRequest and NextResponse APIs

除了支持原生 请求响应 API 之外,Next.js 还使用 NextRequestNextResponse 扩展了它们,为高级用例提供方便的辅助程序。

¥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.tsopengraph-image.tsxicon.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 as page.js.

页面路由结果
app/page.jsapp/route.js冲突
app/page.jsapp/api/route.js有效的
app/[user]/page.jsapp/api/route.js有效的

每个 route.jspage.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/aPromise<{ slug: 'a' }>
app/items/[slug]/route.js/items/bPromise<{ slug: 'b' }>
app/items/[slug]/route.js/items/cPromise<{ 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:

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.xmlrobots.txtapp 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.