路由处理程序
路由处理程序允许你使用 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 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.js
和 layout.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 方法: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
另外还支持原生的 请求 和 响应。Next.js 使用 NextRequest
和 NextResponse
扩展了它们,为高级用例提供方便的辅助程序。
¥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 useNextResponse.json()
for typed responses instead.
选择退出缓存
¥Opting out of caching
你可以通过以下方式选择退出缓存:
¥You can opt out of caching by:
-
将
Request
对象与GET
方法一起使用。¥Using the
Request
object with theGET
method. -
使用任何其他 HTTP 方法。
¥Using any of the other HTTP methods.
-
像使用
cookies
和headers
一样使用 动态函数。¥Using Dynamic Functions like
cookies
andheaders
. -
片段配置选项 手动指定动态模式。
¥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 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.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 中的动态函数一起使用,例如 cookies
和 headers
。
¥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:
要将 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 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.