React Router v7 已發佈。 查看文件
資源路由
本頁內容

資源路由

資源路由不是您應用程式 UI 的一部分,但仍是您應用程式的一部分。它們可以發送任何類型的 Response。

Remix 中的大多數路由都是 UI 路由,也就是實際渲染元件的路由。但路由不一定要渲染元件。在某些情況下,您會希望將路由用作網站的通用端點。以下是一些範例

  • 為重複使用 Remix UI 的伺服器端程式碼的行動應用程式提供 JSON API
  • 動態產生 PDF
  • 為部落格文章或其他頁面動態產生社交圖片
  • 用於其他服務 (如 Stripe 或 GitHub) 的 Webhook
  • 一個 CSS 檔案,動態渲染使用者偏好主題的自訂屬性

建立資源路由

如果路由沒有匯出預設元件,則可以用作資源路由。如果使用 GET 呼叫,則會傳回 loader 的回應,並且也不會呼叫任何父路由 loader (因為這些是 UI 所需的,而這裡不是 UI)。如果使用 POST 呼叫,則會呼叫 action 的回應。

例如,考慮一個渲染報表的 UI 路由,注意連結

export async function loader({
  params,
}: LoaderFunctionArgs) {
  return json(await getReport(params.id));
}

export default function Report() {
  const report = useLoaderData<typeof loader>();
  return (
    <div>
      <h1>{report.name}</h1>
      <Link to="pdf" reloadDocument>
        View as PDF
      </Link>
      {/* ... */}
    </div>
  );
}

它連結到頁面的 PDF 版本。為了實現此目的,我們可以在它下方建立一個資源路由。請注意,它沒有元件:這使其成為資源路由。

export async function loader({
  params,
}: LoaderFunctionArgs) {
  const report = await getReport(params.id);
  const pdf = await generateReportPDF(report);
  return new Response(pdf, {
    status: 200,
    headers: {
      "Content-Type": "application/pdf",
    },
  });
}

當使用者點擊 UI 路由中的連結時,他們將導覽至 PDF。

連結至資源路由

您必須在任何連結至資源路由的連結上使用 reloadDocument

連結至資源路由時,有一個需要注意的細微細節。您需要使用 <Link reloadDocument> 或一般的 <a href> 連結到它。如果您使用普通的 <Link to="pdf"> 連結到它,而不使用 reloadDocument,則資源路由將被視為 UI 路由。Remix 將嘗試使用 fetch 取得資料並渲染元件。別太擔心,如果您犯了這個錯誤,您會收到有用的錯誤訊息。

URL 跳脫

您可能想要將副檔名新增至您的資源路由。這很棘手,因為 Remix 的路由檔案命名慣例之一是 . 會變成 /,因此您可以在不巢狀 UI 的情況下巢狀 URL。

若要將 . 新增至路由的路徑,請使用 [] 跳脫字元。我們的 PDF 路由檔案名稱將變更如下

# original
# /reports/123/pdf
app/routes/reports.$id.pdf.ts

# with a file extension
# /reports/123.pdf
app/routes/reports.$id[.pdf].ts

# or like this, the resulting URL is the same
app/routes/reports.$id[.]pdf.ts

處理不同的請求方法

若要處理 GET 請求,請匯出 loader 函式

import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export const loader = async ({
  request,
}: LoaderFunctionArgs) => {
  // handle "GET" request

  return json({ success: true }, 200);
};

若要處理 POSTPUTPATCHDELETE 請求,請匯出 action 函式

import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno

export const action = async ({
  request,
}: ActionFunctionArgs) => {
  switch (request.method) {
    case "POST": {
      /* handle "POST" */
    }
    case "PUT": {
      /* handle "PUT" */
    }
    case "PATCH": {
      /* handle "PATCH" */
    }
    case "DELETE": {
      /* handle "DELETE" */
    }
  }
};

Webhook

資源路由可用於處理 webhook。例如,您可以建立一個 webhook,在新的提交被推送到儲存庫時接收來自 GitHub 的通知

import crypto from "node:crypto";

import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export const action = async ({
  request,
}: ActionFunctionArgs) => {
  if (request.method !== "POST") {
    return json({ message: "Method not allowed" }, 405);
  }
  const payload = await request.json();

  /* Validate the webhook */
  const signature = request.headers.get(
    "X-Hub-Signature-256"
  );
  const generatedSignature = `sha256=${crypto
    .createHmac("sha256", process.env.GITHUB_WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest("hex")}`;
  if (signature !== generatedSignature) {
    return json({ message: "Signature mismatch" }, 401);
  }

  /* process the webhook (e.g. enqueue a background job) */

  return json({ success: true }, 200);
};
文件和範例依授權條款 MIT