跳至內容

路由處理程序

路由處理程序允許您使用 Web RequestResponse API 為特定路由建立自訂請求處理程序。

Route.js Special File

須知:路由處理程序僅在 app 目錄中可用。它們等同於 pages 目錄中的 API 路由,表示您不需要同時使用 API 路由和路由處理程序。

慣例

路由處理程序定義在 app 目錄中的 route.js|ts 檔案

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

路由處理程序可以巢狀在 app 目錄中,類似於 page.jslayout.js。但不能在與 page.js 相同的路由區段層級中存在 route.js 檔案。

支援的 HTTP 方法

支援下列 HTTP 方法GETPOSTPUTPATCHDELETEHEADOPTIONS。如果呼叫不支援的方法,Next.js 會傳回 405 Method Not Allowed 回應。

擴充 NextRequestNextResponse API

除了支援原生 RequestResponse 之外,Next.js 還透過 NextRequestNextResponse 擴充了它們,以提供便利的輔助函式,用於進階使用案例。

行為

快取

使用 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 警告: Response.json() 僅在 TypeScript 5.2 中有效。如果您使用較低版本的 TypeScript,可以使用 NextResponse.json() 來取得型別化的回應。

取消快取 動態函式,例如 cookiesheaders
  • 透過 區段設定選項 手動指定動態模式。
  • 例如

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

    類似地,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)
    }

    值得注意的是:與 API 路由一樣,路由處理器可用於處理表單提交等情況。目前正在開發一種新的抽象化,用於 處理表單和變異,並與 React 深度整合。

    路由解析

    您可以將 route 視為最低層級的路由原語。

    • 它們不會參與佈局或像 page 一樣的客戶端導覽。
    • page.js 相同的路由中不能route.js 檔案。
    頁面路由結果
    app/page.jsapp/route.js 衝突
    app/page.jsapp/api/route.js 有效
    app/[user]/page.jsapp/api/route.js 有效

    每個 route.jspage.js 檔案都會接管該路由的所有 HTTP 動詞。

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

    範例

    以下範例說明如何將路由處理常式與其他 Next.js API 和功能結合使用。

    重新驗證快取資料

    你可以使用 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)
    }

    或者,你可以使用 revalidate 區段設定選項

    export const revalidate = 60

    動態函式

    路由處理常式可以使用 Next.js 的動態函式,例如 cookiesheaders

    Cookie

    你可以使用 next/headerscookies 來讀取或設定 Cookie。這個伺服器函式可以直接在路由處理常式中呼叫,或嵌套在其他函式中。

    或者,您可以使用 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}` },
      })
    }

    您也可以使用底層 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')
    }

    標頭

    您可以使用 next/headers 中的 headers 讀取標頭。此伺服器函式可以直接在 Route Handler 中呼叫,或嵌套在另一個函式中。

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

    您也可以使用底層 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)
    }

    重新導向

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

    動態路由區段

    建議您在繼續之前閱讀 定義路由 頁面。

    Route Handler 可以使用 動態區段 從動態資料建立請求處理常式。

    app/items/[slug]/route.ts
    export async function GET(
      request: Request,
      { params }: { params: { slug: string } }
    ) {
      const slug = params.slug // 'a', 'b', or 'c'
    }
    路由範例 URL參數
    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 查詢參數

    傳遞給 Route Handler 的請求物件是 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
    }

    串流

    串流通常與大型語言模型 (LLM) 搭配使用,例如 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)
    }

    這些抽象使用 Web API 來建立串流。您也可以直接使用基礎的 Web API。

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

    請求主體

    您可以使用標準的 Web API 方法來讀取 Request 主體

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

    要求主體表單資料

    您可以使用 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 })
    }

    由於 formData 資料都是字串,您可能想要使用 zod-form-data 來驗證要求並以您偏好的格式 (例如 數字) 擷取資料。

    CORS

    您可以使用標準 Web API 方法為特定路由處理常式設定 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',
        },
      })
    }

    好處:

    Webhook

    您可以使用路由處理常式從第三方服務接收 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,
      })
    }

    值得注意的是,與使用 Pages Router 的 API 路由不同,您不需要使用 bodyParser 來使用任何其他組態。

    Edge 和 Node.js 執行時期

    路由處理程式有一個同構的 Web API,可以無縫支援 Edge 和 Node.js 執行時期,包括串流支援。由於路由處理程式使用與 Pages 和 Layouts 相同的 路由區段組態,因此它們支援期待已久的通用 靜態重新產生 的路由處理程式。

    您可以使用 runtime 區段組態選項來指定執行時期

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

    非 UI 回應

    您可以使用路由處理程式來傳回非 UI 內容。請注意 sitemap.xmlrobots.txtapp 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.dev.org.tw/docs</link>
      <description>The React Framework for the Web</description>
    </channel>
     
    </rss>`,
        {
          headers: {
            'Content-Type': 'text/xml',
          },
        }
      )
    }

    區段組態選項

    路由處理程式使用與頁面和版面配置相同的 路由區段組態

    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'

    請參閱 API 參考 以取得更多詳細資料。