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 const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request: Request) {}
export const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request) {}

路由处理程序可以嵌套在 app 目录中,类似于 page.jslayout.js。但不能有 route.js 文件与 page.js 处于同一航段级别。

¥Route Handlers can be nested 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

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

¥In addition to supporting native Request and Response. Next.js extends them with NextRequest and NextResponse to provide convenient helpers for advanced use cases.

行为

¥Behavior

缓存

¥Caching

当将 GET 方法与 Response 对象一起使用时,默认情况下会缓存路由处理程序。

¥Route Handlers are cached by default when using the GET method with the Response object.

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 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 })
}

TypeScript 警告:Response.json() 仅从 TypeScript 5.2 起有效。如果你使用较低的 TypeScript 版本,则可以使用 NextResponse.json() 来代替键入的响应。

¥TypeScript Warning: Response.json() is only valid from TypeScript 5.2. If you use a lower TypeScript version, you can use NextResponse.json() for typed responses instead.

选择退出缓存

¥Opting out of caching

你可以通过以下方式选择退出缓存:

¥You can opt out of caching by:

  • Request 对象与 GET 方法一起使用。

    ¥Using the Request object with the GET method.

  • 使用任何其他 HTTP 方法。

    ¥Using any of the other HTTP methods.

  • 像使用 cookiesheaders 一样使用 动态函数

    ¥Using Dynamic Functions like cookies and headers.

  • 片段配置选项 手动指定动态模式。

    ¥The Segment Config Options manually specifies dynamic mode.

例如:

¥For example:

export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY!,
},
})
const product = await res.json()

return Response.json({ product })
}
export async function GET(request) {
const { searchParams } = new URL(request.url)
const id = searchParams.get('id')
const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const product = await res.json()

return Response.json({ product })
}

同样,POST 方法将导致路由处理程序被动态评估。

¥Similarly, the POST method will cause the Route Handler to be evaluated dynamically.

export async function POST() {
const res = await fetch('https://data.mongodb-api.com/...', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY!,
},
body: JSON.stringify({ time: new Date().toISOString() }),
})

const data = await res.json()

return Response.json(data)
}
export async function POST() {
const res = await fetch('https://data.mongodb-api.com/...', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
body: JSON.stringify({ time: new Date().toISOString() }),
})

const data = await res.json()

return Response.json(data)
}

很高兴知道:与 API 路由一样,路由处理程序可用于处理表单提交等情况。与 React 深度集成的 处理形式和突变 新抽象正在开发中。

¥Good to know: Like API Routes, Route Handlers can be used for cases like handling form submissions. A new abstraction for handling forms and mutations that integrates deeply with React is being worked on.

路由解析

¥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.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

你可以使用 next.revalidate 选项进行 重新验证缓存数据

¥You can revalidate cached data using the next.revalidate option:

export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 }, // Revalidate every 60 seconds
})
const data = await res.json()

return Response.json(data)
}
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 }, // Revalidate every 60 seconds
})
const data = await res.json()

return Response.json(data)
}

或者,你可以使用 revalidate 段配置选项

¥Alternatively, you can use the revalidate segment config option:

export const revalidate = 60

动态函数

¥Dynamic Functions

路由处理程序可以与 Next.js 中的动态函数一起使用,例如 cookiesheaders

¥Route Handlers can be used with dynamic functions from Next.js, like cookies and headers.

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 = 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 = 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 = 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 = 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: { slug: string } }
) {
const slug = params.slug // 'a', 'b', or 'c'
}
export async function GET(request, { params }) {
const slug = params.slug // 'a', 'b', or 'c'
}
路由示例网址params
app/items/[slug]/route.js/items/a{ slug: 'a' }
app/items/[slug]/route.js/items/b{ slug: 'b' }
app/items/[slug]/route.js/items/c{ 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 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})

export async function POST(req: Request) {
const { messages } = await req.json()
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
})

const stream = OpenAIStream(response)

return new StreamingTextResponse(stream)
}
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})

export async function POST(req) {
const { messages } = await req.json()
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
})

const stream = OpenAIStream(response)

return new StreamingTextResponse(stream)
}

这些抽象使用 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 const dynamic = 'force-dynamic' // defaults to auto

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 const dynamic = 'force-dynamic' // defaults to auto

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 const dynamic = 'force-dynamic' // defaults to auto

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 const dynamic = 'force-dynamic' // defaults to auto

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.