“深入淺出 NextJS Middleware 用途說明”


NextJS Middleware 是什麼?

嘿!你有沒有想過在你的 NextJS 應用程式中,每次使用者請求時都能先經過一段中間程序來處理驗證、日誌記錄或其他重要的操作?這就是 Middleware 的用途!它就像是一個守門員,讓你可以在請求抵達最終目的地之前先做些處理工作。

Middleware 在 NextJS 中有很多實用的應用,今天我們就來深入探討它們的用途、如何使用,以及一些實際的應用範例。

Middleware 的用途

Middleware 可以應用在很多場景,包括但不限於:

  • 驗證和授權:檢查使用者是否已經登入,並根據其角色授予適當的權限。
  • 日誌記錄:記錄每個請求的細節,幫助你追蹤應用程式的使用情況。
  • 重定向:根據特定條件將使用者重定向到不同的頁面。
  • 處理跨站請求:防止跨站請求偽造(CSRF)攻擊。
  • 緩存:對某些靜態資源進行緩存,提高網站的效能。

讓我們透過一些實際的例子來看看如何在 NextJS 中實現這些功能。

如何在 NextJS 中使用 Middleware

NextJS 提供了一個簡單而強大的方式來使用 Middleware。只需在 pages 目錄或其子目錄中創建 _middleware.ts 文件,即可定義中介軟體。

範例:全域性驗證 Middleware

首先,我們來看看如何創建一個全域性的驗證 Middleware,確保所有未經驗證的請求都被重定向到登入頁面。

import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';

export async function middleware(req: NextRequest) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });

  const url = req.nextUrl.clone();

  if (!token && !url.pathname.startsWith('/auth')) {
    url.pathname = '/auth/login';
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

這段代碼會在每個請求被處理之前檢查是否有有效的 JWT token。如果沒有,就將請求重定向到 /auth/login 頁面。

範例:特定路由的 Middleware

有時候,你可能只想對某些特定路由應用 Middleware。例如,假設我們只想對 /dashboard 路由應用驗證邏輯,我們可以這樣做:

// pages/dashboard/_middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';

export async function middleware(req: NextRequest) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });

  const url = req.nextUrl.clone();

  if (!token) {
    url.pathname = '/auth/login';
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}

export const config = {
  matcher: [
    '/dashboard/:path*',
  ],
};

這樣一來,只有訪問 /dashboard 路由的請求會被這個 Middleware 處理。

更進一步:Middleware 與其他功能結合

使用 Middleware 進行日誌記錄

Middleware 也可以用來記錄請求的詳細信息,這對於排錯和監控應用程式非常有幫助。下面是一個簡單的日誌記錄 Middleware 範例:

// middleware/logger.ts
import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  console.log(`Request received: ${req.method} ${req.url}`);
  return NextResponse.next();
}

export const config = {
  matcher: [
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

使用 Middleware 進行重定向

有時候,我們需要根據特定條件重定向使用者。例如,當使用者訪問舊版本的網站時,我們希望將他們重定向到新版本的頁面:

// middleware/redirect.ts
import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  const url = req.nextUrl.clone();

  if (url.pathname.startsWith('/old-version')) {
    url.pathname = '/new-version';
    return NextResponse.redirect(url);
  }

  return NextResponse.next();
}

export const config = {
  matcher: [
    '/old-version/:path*',
  ],
};

_middleware.tsx 與目錄 middleware 的差異

在 NextJS 中,使用 _middleware.tsx 和在目錄 middleware 中定義中介軟體有一些關鍵的差異。

_middleware.tsx

  • 自動應用:在 pages 目錄或其子目錄中創建 _middleware.tsx 文件,這些中介軟體會自動應用到該目錄及其子目錄的所有路由。
  • 簡單配置:你只需定義一次中介軟體,NextJS 會自動識別並應用它。
  • 適用範圍:可以全域性應用,也可以針對特定路由應用。

目錄 middleware

  • 自定義組織:這是一個用來組織和管理中介軟體的目錄,可以包含多個中介軟體文件。
  • 需要手動引用:需要在特定的路由或 API 處理程序中手動引用這些中介軟體。
  • 靈活使用:適合用來管理多個不同用途的中介軟體,如驗證、日誌記錄等,並可以根據需求選擇性應用。

差異對照表

_middleware.tsxmiddleware 目錄
自動應用
手動引用
適用範圍目錄及其子目錄的所有路由需要手動選擇應用
靈活性中等
組織管理適中

結論

NextJS 的 Middleware 是一個強大且靈活的工具,能讓你在應用程式的請求流程中插入自定義邏輯,從而實現驗證、日誌記錄、重定向等功能。透過合理地使用 Middleware,你可以顯著提升應用程式的安全性和效能。

希望這篇文章能幫助你更好地理解和使用 NextJS 的 Middleware。如果你有任何問題或需要進一步的幫助,歡迎隨時聯繫我。Happy coding!


Middleware 用途對照表

用途描述範例
驗證確認使用者是否登入,並根據角色授予權限檢查 JWT token,重定向到登入頁面
日誌記錄記錄每個請求的細節,便於追蹤應用程式的使用情況記錄請求方法和 URL
重定向根據特定條件重定向使用者到不同的頁面將訪問舊版本頁面的請求重定向到新版本
防跨站請求防止跨站請求偽造(CSRF)攻擊檢查和驗證 CSRF token
緩存對某些靜態資源進行緩存,提高網站效能設置緩存標頭來優化

資源加載 |


流程圖如下:

graph TD
    A[訪客] -->|發送請求| B[Next.js 伺服器]
    B --> C{是否有 _middleware.ts?}
    C -->|是| D[執行 _middleware.ts]
    C -->|否| E{是否有對應頁面?}
    D --> F{中介軟體處理完成?}
    F -->|是| E
    F -->|否| G[中止請求或重定向]
    E -->|是| H[執行頁面邏輯]
    E -->|否| I[返回 404 頁面]
    H --> J[返回響應]
    I --> J
    G --> J

希望這篇文章對你有幫助!如果你有任何問題或想法,歡迎在留言區分享。Happy coding!