Skip to content

前端后端

¥Backend for Frontend

Next.js 支持 "前端后端" 模式。这允许你创建公共端点来处理 HTTP 请求并返回任何内容类型,而不仅仅是 HTML。你还可以访问数据源并执行副作用,例如更新远程数据。

¥Next.js supports the "Backend for Frontend" pattern. This lets you create public endpoints to handle HTTP requests and return any content type—not just HTML. You can also access data sources and perform side effects like updating remote data.

如果你正在启动一个新项目,使用带有 --api 标志的 create-next-app 会自动在新项目的 app/ 文件夹中包含一个示例 route.ts,演示如何创建 API 端点。

¥If you are starting a new project, using create-next-app with the --api flag automatically includes an example route.ts in your new project’s app/ folder, demonstrating how to create an API endpoint.

bash
npx create-next-app@latest --api

需要了解:Next.js 后端功能并非完全替代后端。它们充当 API 层,用于:

¥Good to know: Next.js backend capabilities are not a full backend replacement. They serve as an API layer that:

  • 可公开访问

    ¥is publicly reachable

  • 处理任何 HTTP 请求

    ¥handles any HTTP request

  • 可以返回任何内容类型

    ¥can return any content type

要实现此模式,请使用:

¥To implement this pattern, use:

公共端点

¥Public Endpoints

路由处理程序是公共 HTTP 端点。任何客户端都可以访问它们。

¥Route Handlers are public HTTP endpoints. Any client can access them.

使用 route.tsroute.js 文件约定创建路由处理程序:

¥Create a Route Handler using the route.ts or route.js file convention:

ts
export function GET(request: Request) {}

这将处理发送到 /apiGET 请求。

¥This handles GET requests sent to /api.

对可能引发异常的操作使用 try/catch 块:

¥Use try/catch blocks for operations that may throw an exception:

ts
import { submit } from '@/lib/submit'

export function POST(request: Request) {
  try {
    await submit(request)
    return new Response(null, { status: 204 })
  } catch (reason) {
    const message =
      reason instanceof Error ? reason.message : 'Unexpected error'

    return new Response(message, { status: 500 })
  }
}

避免在发送给客户端的错误消息中暴露敏感信息。

¥Avoid exposing sensitive information in error messages sent to the client.

为限制访问,请实现身份验证和授权。查看 验证

¥To restrict access, implement authentication and authorization. See Authentication.

内容类型

¥Content types

路由处理程序允许你提供非 UI 响应,包括 JSON、XML、图片、文件和纯文本。

¥Route Handlers let you serve non-UI responses, including JSON, XML, images, files, and plain text.

Next.js 对常用端点使用文件约定:

¥Next.js uses file conventions for common endpoints:

你还可以定义自定义选项,例如:

¥You can also define custom ones, such as:

  • llms.txt

  • rss.xml

  • .well-known

例如,app/rss.xml/route.tsrss.xml 创建了一个路由处理程序。

¥For example, app/rss.xml/route.ts creates a Route Handler for rss.xml.

ts
export async function GET(request: Request) {
  const rssResponse = await fetch(/* rss endpoint */)
  const rssData = await rssResponse.json()

  const rssFeed = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
 <title>${rssData.title}</title>
 <description>${rssData.description}</description>
 <link>${rssData.link}</link>
 <copyright>${rssData.copyright}</copyright>
 ${rssData.items.map((item) => {
   return `<item>
    <title>${item.title}</title>
    <description>${item.description}</description>
    <link>${item.link}</link>
    <pubDate>${item.publishDate}</pubDate>
    <guid isPermaLink="false">${item.guid}</guid>
 </item>`
 })}
</channel>
</rss>`

  const headers = new Headers({ 'content-type': 'application/xml' })

  return new Response(rssFeed, { headers })
}

过滤用于生成标记的任何输入。

¥Sanitize any input used to generate markup.

使用请求负载

¥Consuming request payloads

使用请求 实例方法(例如 .json().formData().text())来访问请求主体。

¥Use Request instance methods like .json(), .formData(), or .text() to access the request body.

GETHEAD 请求不包含正文。

¥GET and HEAD requests don’t carry a body.

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

需要了解:将数据传递给其他系统前请验证数据。

¥Good to know: Validate data before passing it to other systems

ts
import { sendMail, validateInputs } from '@/lib/email-transporter'

export async function POST(request: Request) {
  const formData = await request.formData()
  const email = formData.get('email')
  const contents = formData.get('contents')

  try {
    await validateInputs({ email, contents })
    const info = await sendMail({ email, contents })

    return Response.json({ messageId: info.messageId })
  } catch (reason) {
    const message =
      reason instanceof Error ? reason.message : 'Unexpected exception'

    return new Response(message, { status: 500 })
  }
}

你只能读取一次请求主体。如果需要再次读取请求,请克隆该请求:

¥You can only read the request body once. Clone the request if you need to read it again:

ts
export async function POST(request: Request) {
  try {
    const clonedRequest = request.clone()

    await request.body()
    await clonedRequest.body()
    await request.body() // Throws error

    return new Response(null, { status: 204 })
  } catch {
    return new Response(null, { status: 500 })
  }
}

数据操作

¥Manipulating data

路由处理程序可以转换、过滤和聚合来自一个或多个来源的数据。这样可以将逻辑隔离在前端之外,避免暴露内部系统。

¥Route Handlers can transform, filter, and aggregate data from one or more sources. This keeps logic out of the frontend and avoids exposing internal systems.

你还可以将繁重的计算任务转移到服务器,从而减少客户端的电池和数据使用量。

¥You can also offload heavy computations to the server and reduce client battery and data usage.

ts
import { parseWeatherData } from '@/lib/weather'

export async function POST(request: Request) {
  const body = await request.json()
  const searchParams = new URLSearchParams({ lat: body.lat, lng: body.lng })

  try {
    const weatherResponse = await fetch(`${weatherEndpoint}?${searchParams}`)

    if (!weatherResponse.ok) {
      /* handle error */
    }

    const weatherData = await weatherResponse.text()
    const payload = parseWeatherData.asJSON(weatherData)

    return new Response(payload, { status: 200 })
  } catch (reason) {
    const message =
      reason instanceof Error ? reason.message : 'Unexpected exception'

    return new Response(message, { status: 500 })
  }
}

需要了解:本示例使用 POST 来避免将地理位置数据放入 URL 中。GET 请求可能会被缓存或记录,这可能会暴露敏感信息。

¥Good to know: This example uses POST to avoid putting geo-location data in the URL. GET requests may be cached or logged, which could expose sensitive info.

代理到后端

¥Proxying to a backend

你可以使用路由处理程序作为另一个后端的代理。在转发请求之前添加验证逻辑。

¥You can use a Route Handler as a proxy to another backend. Add validation logic before forwarding the request.

ts
import { isValidRequest } from '@/lib/utils'

export async function POST(request: Request, { params }) {
  const clonedRequest = request.clone()
  const isValid = await isValidRequest(clonedRequest)

  if (!isValid) {
    return new Response(null, { status: 400, statusText: 'Bad Request' })
  }

  const { slug } = await params
  const pathname = slug.join('/')
  const proxyURL = new URL(pathname, 'https://next.nodejs.cn')
  const proxyRequest = new Request(proxyURL, request)

  try {
    return fetch(proxyRequest)
  } catch (reason) {
    const message =
      reason instanceof Error ? reason.message : 'Unexpected exception'

    return new Response(message, { status: 500 })
  }
}

或使用:

¥Or use:

NextRequest 和 NextResponse

¥NextRequest and NextResponse

Next.js 使用简化常见操作的方法扩展了 RequestResponse Web API。这些扩展在路由处理程序和中间件中均可用。

¥Next.js extends the Request and Response Web APIs with methods that simplify common operations. These extensions are available in both Route Handlers and Middleware.

两者都提供了读取和操作 Cookie 的方法。

¥Both provide methods for reading and manipulating cookies.

NextRequest 包含 nextUrl 属性,该属性公开传入请求的解析值,例如,它使访问请求路径名和搜索参数变得更加容易。

¥NextRequest includes the nextUrl property, which exposes parsed values from the incoming request, for example, it makes it easier to access request pathname and search params.

NextResponse 提供 next()json()redirect()rewrite() 等辅助函数。

¥NextResponse provides helpers like next(), json(), redirect(), and rewrite().

你可以将 NextRequest 传递给任何需要 Request 的函数。同样,你可以在需要 Response 的地方返回 NextResponse

¥You can pass NextRequest to any function expecting Request. Likewise, you can return NextResponse where a Response is expected.

ts
import { type NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const nextUrl = request.nextUrl

  if (nextUrl.searchParams.get('redirect')) {
    return NextResponse.redirect(new URL('/', request.url))
  }

  if (nextUrl.searchParams.get('rewrite')) {
    return NextResponse.rewrite(new URL('/', request.url))
  }

  return NextResponse.json({ pathname: nextUrl.pathname })
}

了解更多关于 NextRequestNextResponse 的信息。

¥Learn more about NextRequest and NextResponse.

Webhook 和回调 URL

¥Webhooks and callback URLs

使用路由处理程序接收来自第三方应用的事件通知。

¥Use Route Handlers to receive event notifications from third-party applications.

例如,在 CMS 中,当内容发生变化时重新验证路由。配置 CMS 以在发生更改时调用特定端点。

¥For example, revalidate a route when content changes in a CMS. Configure the CMS to call a specific endpoint on changes.

ts
import { type NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const token = request.nextUrl.searchParams.get('token')

  if (token !== process.env.REVALIDATE_SECRET_TOKEN) {
    return NextResponse.json({ success: false }, { status: 401 })
  }

  const tag = request.nextUrl.searchParams.get('tag')

  if (!tag) {
    return NextResponse.json({ success: false }, { status: 400 })
  }

  revalidateTag(tag)

  return NextResponse.json({ success: true })
}

回调 URL 是另一个用例。当用户完成第三方流程时,第三方会将其发送到回调 URL。使用路由处理程序验证响应并决定将用户重定向到何处。

¥Callback URLs are another use case. When a user completes a third-party flow, the third party sends them to a callback URL. Use a Route Handler to verify the response and decide where to redirect the user.

ts
import { type NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const token = request.nextUrl.searchParams.get('session_token')
  const redirectUrl = request.nextUrl.searchParams.get('redirect_url')

  const response = NextResponse.redirect(new URL(redirectUrl, request.url))

  response.cookies.set({
    value: token,
    name: '_token',
    path: '/',
    secure: true,
    httpOnly: true,
    expires: undefined, // session cookie
  })

  return response
}

重定向

¥Redirects

ts
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  redirect('https://next.nodejs.cn/')
}

了解有关 redirectpermanentRedirect 中重定向的更多信息

¥Learn more about redirects in redirect and permanentRedirect

中间件

¥Middleware

每个项目只允许一个中间件文件。使用 config.matcher 定位特定路径。了解有关 中间件 的更多信息。

¥Only one middleware file is allowed per project. Use config.matcher to target specific paths. Learn more about middleware.

使用 middleware 在请求到达路由路径之前生成响应。

¥Use middleware to generate a response before the request reaches a route path.

ts
import { isAuthenticated } from '@lib/auth'

export const config = {
  matcher: '/api/:function*',
}

export function middleware(request: Request) {
  if (!isAuthenticated(request)) {
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}

你还可以使用 middleware 代理请求:

¥You can also proxy requests using middleware:

ts
import { NextResponse } from 'next/server'

export function middleware(request: Request) {
  if (request.nextUrl.pathname === '/proxy-this-path') {
    const rewriteUrl = new URL('https://next.nodejs.cn')
    return NextResponse.rewrite(rewriteUrl)
  }
}

middleware 可以产生的另一种响应类型是重定向:

¥Another type of response middleware can produce are redirects:

ts
import { NextResponse } from 'next/server'

export function middleware(request: Request) {
  if (request.nextUrl.pathname === '/v1/docs') {
    request.nextUrl.pathname = '/v2/docs'
    return NextResponse.redirect(request.nextUrl)
  }
}

安全

¥Security

速率限制

¥Rate limiting

你可以在 Next.js 后端实现速率限制。除了基于代码的检查外,还要启用主机提供的任何速率限制功能。

¥You can implement rate limiting in your Next.js backend. In addition to code-based checks, enable any rate limiting features provided by your host.

ts
import { NextResponse } from 'next/server'
import { checkRateLimit } from '@/lib/rate-limit'

export async function POST(request: Request) {
  const { rateLimited } = await checkRateLimit(request)

  if (rateLimited) {
    return NextResponse.json({ error: 'Rate limit exceeded' }, { status: 429 })
  }

  return new Response(null, { status: 204 })
}

验证有效负载

¥Verify payloads

切勿信任传入的请求数据。使用前请验证内容类型和大小,并进行 XSS 防护。

¥Never trust incoming request data. Validate content type and size, and sanitize against XSS before use.

使用超时来防止滥用并保护服务器资源。

¥Use timeouts to prevent abuse and protect server resources.

将用户生成的静态资源存储在专用服务中。如果可能,请从浏览器上传它们,并将返回的 URI 存储在数据库中,以减少请求大小。

¥Store user-generated static assets in dedicated services. When possible, upload them from the browser and store the returned URI in your database to reduce request size.

访问受保护资源

¥Access to protected resources

授予访问权限前务必验证凭据。不要仅依赖中间件进行身份验证和授权。

¥Always verify credentials before granting access. Do not rely on middleware alone for authentication and authorization.

从响应和后端日志中删除敏感或不必要的数据。

¥Remove sensitive or unnecessary data from responses and backend logs.

定期轮换凭证和 API 密钥。

¥Rotate credentials and API keys regularly.

预检请求

¥Preflight Requests

预检请求使用 OPTIONS 方法根据来源、方法和标头询问服务器是否允许请求。

¥Preflight requests use the OPTIONS method to ask the server if a request is allowed based on origin, method, and headers.

如果未定义 OPTIONS,Next.js 会自动添加它,并根据其他已定义的方法设置 Allow 标头。

¥If OPTIONS is not defined, Next.js adds it automatically and sets the Allow header based on the other defined methods.

库模式

¥Library patterns

社区库通常使用工厂模式来处理路由处理程序。

¥Community libraries often use the factory pattern for Route Handlers.

ts
import { createHandler } from 'third-party-library'

const handler = createHandler({
  /* library-specific options */
})

export const GET = handler
// or
export { handler as POST }

这会为 GETPOST 请求创建一个共享处理程序。该库根据请求中的 methodpathname 自定义行为。

¥This creates a shared handler for GET and POST requests. The library customizes behavior based on the method and pathname in the request.

库也可以提供 middleware 工厂。

¥Libraries can also provide a middleware factory.

ts
import { createMiddleware } from 'third-party-library'

export default createMiddleware()

更多示例

¥More examples

查看更多关于使用 路由处理程序middleware API 参考的示例。

¥See more examples on using Router Handlers and the middleware API references.

这些示例包括与 Cookies标头流式、中间件 否定匹配 以及其他有用的代码片段配合使用。

¥These examples include, working with Cookies, Headers, Streaming, Middleware negative matching, and other useful code snippets.

注意事项

¥Caveats

服务器组件

¥Server Components

服务器组件直接从其源获取数据,而不是通过路由处理程序获取。

¥Fetch data in Server Components directly from its source, not via Route Handlers.

对于在构建时预渲染的服务器组件,使用路由处理程序将导致构建步骤失败。这是因为在构建时没有服务器监听这些请求。

¥For Server Components pre-rendered at build time, using Route Handlers will fail the build step. This is because, while building there is no server listening for these requests.

对于按需渲染的服务器组件,由于处理程序和渲染进程之间额外的 HTTP 往返,从路由处理程序获取数据的速度较慢。

¥For Server Components rendered on demand, fetching from Route Handlers is slower due to the extra HTTP round trip between the handler and the render process.

服务器端 fetch 请求使用绝对 URL。这意味着需要进行一次 HTTP 往返,即到外部服务器的往返。在开发过程中,你自己的开发服务器将充当外部服务器。构建时没有服务器,运行时,服务器可通过面向公众的域访问。

¥A server side fetch request uses absolute URLs. This implies an HTTP round trip, to an external server. During development, your own development server acts as the external server. At build time there is no server, and at runtime, the server is available through your public facing domain.

服务器组件涵盖了大多数数据获取需求。但是,在客户端获取数据可能有必要:

¥Server Components cover most data-fetching needs. However, fetching data client side might be necessary for:

  • 依赖于仅客户端 Web API 的数据:

    ¥Data that depends on client-only Web APIs:

    • 地理位置 API

      ¥Geo-location API

    • 存储 API

      ¥Storage API

    • 音频 API

      ¥Audio API

    • 文件 API

      ¥File API

  • 频繁轮询的数据

    ¥Frequently polled data

对于这些,请使用像 swrreact-query 这样的社区库。

¥For these, use community libraries like swr or react-query.

服务器操作

¥Server Actions

服务器操作允许你从客户端运行服务器端代码。它们的主要目的是改变来自前端客户端的数据。

¥Server Actions let you run server-side code from the client. Their primary purpose is to mutate data from your frontend client.

服务器操作已排队。使用它们进行数据获取会引入顺序执行。

¥Server Actions are queued. Using them for data fetching introduces sequential execution.

export 模式

¥export mode

export 模式输出一个没有运行时服务器的静态站点。需要 Next.js 运行时的功能是 不支持,因为此模式会生成静态站点,而无需运行时服务器。

¥export mode outputs a static site without a runtime server. Features that require the Next.js runtime are not supported, because this mode produces a static site, and no runtime server.

export mode 中,仅支持 GET 路由处理程序,并与 dynamic 路由段配置结合使用,设置为 'force-static'

¥In export mode, only GET Route Handlers are supported, in combination with the dynamic route segment config, set to 'force-static'.

这可用于生成静态 HTML、JSON、TXT 或其他文件。

¥This can be used to generate static HTML, JSON, TXT, or other files.

部署环境

¥Deployment environment

一些主机将路由处理程序部署为 lambda 函数。这意味着:

¥Some hosts deploy Route Handlers as lambda functions. This means:

  • 路由处理程序无法在请求之间共享数据。

    ¥Route Handlers cannot share data between requests.

  • 环境可能不支持写入文件系统。

    ¥The environment may not support writing to File System.

  • 长时间运行的处理程序可能会因超时而终止。

    ¥Long-running handlers may be terminated due to timeouts.

  • WebSockets 将无法工作,因为连接会在超时或生成响应后关闭。

    ¥WebSockets won’t work because the connection closes on timeout, or after the response is generated.