Route Handlers 路由处理程序

Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.

路由处理程序允许您使用 Web 请求和响应 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.

温馨提示:路由处理程序仅在 app 目录中可用。它们相当于 pages 目录中的 API 路由,这意味着您无需同时使用 API 路由和路由处理程序。

1.Convention 约定

Route Handlers are defined in a route.js|ts file inside the app directory:

路由处理程序在 app 目录中的 route.js|ts 文件中定义:

// app/api/route.ts
export const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request: Request) {}

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.

路由处理程序可以嵌套在 app 目录中,类似于 page.js 和 layout.js 。但不能在与 page.js 相同的路由段级别存在 route.js 文件。

a.Supported HTTP Methods 支持的 HTTP 方法

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.

支持以下 HTTP 方法: GET 、 POST 、 PUT 、 PATCH 、 DELETE 、 HEAD 和 OPTIONS 。如果调用了不支持的方法,Next.js 将返回 405 Method Not Allowed 响应。

b.Extended NextRequest and NextResponse APIs 扩展的 NextRequest 和 NextResponse API

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

除了支持本机 Request 和 Response 外,Next.js 还通过 NextRequest 和 NextResponse 对它们进行了扩展,以便为高级用例提供便捷的帮助程序。

2.Behavior 行为

a.Caching 缓存

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

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

// app/items/route.ts
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 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.

TypeScript 警告: Response.json() 仅在 TypeScript 5.2 中有效。如果您使用较低版本的 TypeScript,则可以使用 NextResponse.json() 来代替类型化响应。

b.Opting out of caching 选择退出缓存

You can opt out of caching by:

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

  • Using the Request object with the GET method.
    使用带有 GET 方法的 Request 对象。

  • Using any of the other HTTP methods.
    使用任何其他 HTTP 方法。

  • Using Dynamic Functions like cookies and headers.
    使用动态函数,如 cookiesheaders

  • The Segment Config Options manually specifies dynamic mode.
    分段配置选项手动指定动态模式。

For example: 例如:

// app/products/api/route.ts
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 })
}

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

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

// app/items/route.ts
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)
}

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.

值得注意的是:与 API 路由一样,路由处理程序可用于处理表单提交等情况。目前正在开发一种新的抽象方法,用于处理与 React 深度集成的表单和突变。

c.Route Resolution 路由解析

You can consider a route the lowest level routing primitive.

您可以将 route 视为最低级别的路由基元。

  • They do not participate in layouts or client-side navigations like page.
    它们不参与布局或客户端导航,例如 page

  • There cannot be a route.js file at the same route as page.js.
    在与 page.js 相同的路由中不能有 route.js 文件。

Page 

Route 

Result 

app/page.js

app/route.js

Conflict 冲突

app/page.js

app/api/route.js

Valid 有效

app/[user]/page.js

app/api/route.js

Valid 有效

Each route.js or page.js file takes over all HTTP verbs for that route.

每个 route.js 或 page.js 文件都会接管该路由的所有 HTTP 动词。

// app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}
 
// ❌ Conflict
// `app/route.js`
export async function POST(request) {}

3.Examples 示例

The following examples show how to combine Route Handlers with other Next.js APIs and features.

以下示例展示了如何将路由处理程序与其他 Next.js API 和功能结合使用。

a.Revalidating Cached Data 重新验证缓存数据

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

您可以使用 next.revalidate 选项重新验证缓存数据:

// app/items/route.ts
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)
}

Alternatively, you can use the revalidate segment config option:

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

export const revalidate = 60

b.Dynamic Functions 动态函数

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

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

Ⅰ.Cookies

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.

您可以使用 cookies 从 next/headers 读取或设置 cookie。此服务器函数可以直接在路由处理程序中调用,或嵌套在另一个函数中。

Alternatively, you can return a new Response using the Set-Cookie header.

或者,您可以使用 Set-Cookie 标头返回新的 Response 。

// app/api/route.ts
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}` },
  })
}

You can also use the underlying Web APIs to read cookies from the request (NextRequest):

您还可以使用底层 Web API 从请求中读取 cookie( NextRequest ):

// app/api/route.ts
import { type NextRequest } from 'next/server'
 
export async function GET(request: NextRequest) {
  const token = request.cookies.get('token')
}
Ⅱ.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 从 next/headers 读取标头。此服务器函数可以直接在路由处理程序中调用,或嵌套在另一个函数中。

This headers instance is read-only. To set headers, you need to return a new Response with new headers.

此 headers 实例是只读的。要设置标头,您需要返回一个带有新 headers 的新 Response 。

// app/api/route.ts
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 },
  })
}

You can also use the underlying Web APIs to read headers from the request (NextRequest):

您还可以使用底层 Web API 从请求中读取标头 ( NextRequest ):

// app/api/route.ts
import { type NextRequest } from 'next/server'
 
export async function GET(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
}

c.Redirects 重定向

// app/api/route.ts
import { redirect } from 'next/navigation'
 
export async function GET(request: Request) {
  redirect('https://nextjs.org/')
}

d.Dynamic Route Segments 动态路由段

Route Handlers can use Dynamic Segments to create request handlers from dynamic data.

路由处理程序可以使用动态段从动态数据创建请求处理程序。

// app/items/[slug]/route.ts
export async function GET(
  request: Request,
  { params }: { params: { slug: string } }
) {
  const slug = params.slug // 'a', 'b', or 'c'
}

Route

Example URL

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

e.URL Query Parameters URL 查询参数

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.

传递给路由处理程序的请求对象是一个 NextRequest 实例,它有一些额外的便捷方法,包括更轻松地处理查询参数。

// app/api/search/route.ts
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
}

f.Streaming 流媒体

Streaming is commonly used in combination with Large Language Models (LLMs), such as OpenAI, for AI-generated content. Learn more about the AI SDK.

流式传输通常与大型语言模型(LLMs)结合使用,例如 OpenAI,用于生成 AI 内容。详细了解 AI SDK。

// app/api/chat/route.ts
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'
 
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})
 
export const runtime = 'edge'
 
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)
}

These abstractions use the Web APIs to create a stream. You can also use the underlying Web APIs directly.

这些抽象使用 Web API 来创建流。您也可以直接使用底层 Web API。

// https://developer.mozilla.org/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)
}

g.Request Body 请求正文

You can read the Request body using the standard Web API methods:

您可以使用标准 Web API 方法读取 Request 正文:

// app/items/route.ts
export async function POST(request: Request) {
  const res = await request.json()
  return Response.json({ res })
}

h.Request Body FormData 请求正文 FormData

You can read the FormData using the request.formData() function:

您可以使用 request.formData() 函数读取 FormData :

// app/items/route.ts
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 })
}

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

由于 formData 数据都是字符串,因此您可能希望使用 zod-form-data 来验证请求并以您喜欢的格式检索数据(例如 number )。

i.CORS

You can set CORS headers on a Response using the standard Web API methods:

您可以使用标准 Web API 方法在 Response 上设置 CORS 头:

// app/api/route.ts
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',
    },
  })
}

j.Webhooks 网络挂钩

You can use a Route Handler to receive webhooks from third-party services:

您可以使用路由处理程序来接收来自第三方服务的 Webhook:

// app/api/route.ts
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,
  })
}

Notably, unlike API Routes with the Pages Router, you do not need to use bodyParser to use any additional configuration.

值得注意的是,与使用 Pages 路由器的 API 路由不同,您无需使用 bodyParser 来使用任何其他配置。

k.Edge and Node.js Runtimes Edge 和 Node.js 运行时

Route Handlers have an isomorphic Web API to support both Edge and Node.js runtimes seamlessly, including support for streaming. Since Route Handlers use the same route segment configuration as Pages and Layouts, they support long-awaited features like general-purpose statically regenerated Route Handlers.

路由处理程序具有同构 Web API,可无缝支持 Edge 和 Node.js 运行时,包括对流的支持。由于路由处理程序使用与 Pages 和布局相同的路由段配置,因此它们支持期待已久的通用静态重新生成路由处理程序等功能。

You can use the runtime segment config option to specify the runtime:

您可以使用 runtime 段配置选项来指定运行时:

export const runtime = 'edge' // 'nodejs' is the default

l.Non-UI Responses 非 UI 响应

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.

您可以使用路由处理程序返回非 UI 内容。请注意, sitemap.xml 、 robots.txt 、 app icons 和开放图象都具有内置支持。

// app/rss.xml/route.ts
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://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>
 
</rss>`,
    {
      headers: {
        'Content-Type': 'text/xml',
      },
    }
  )
}

m.Segment Config Options 段配置选项

Route Handlers use the same route segment configuration as pages and layouts.

路由处理程序使用与页面和布局相同的路由段配置。

// app/items/route.ts
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'