iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Modern Web

從 React 學 Next.js:不只要會用,還要真的懂系列 第 21

【Day 21】Middleware 是什麼?伺服器層級的權限控制

  • 分享至 

  • xImage
  •  

這幾天我們連續看了一些 Router 的用法,今天我們稍微偏離 Router 一下,從用 Router 設定權限控制的實作情境,延伸到另一個不同層面的權限控管方式,也就是 「透過 Middleware 來實作權限控管」的部分。除了看要怎麼透過 Middleware 控制權限外,也會來認識一下 Middleware。
那就直接入正題!

什麼是 Middleware?

「Middleware」這個詞,從文字面來看意思是「中介軟體」或「中介層」,再進一步說明的話,指的是在兩個邏輯層或兩個模組之間,進行一些額外的邏輯處理。

Middleware 這個概念不只使用在 Next.js 這個框架中,也被應用於其他實務中,例如 API 系統、資料交換流程等。

在 Next.js 中,Middleware 是在伺服器接收到使用者請求之後,進入對應頁面或 API 處理之前所執行的中介邏輯。換句話說,它是在頁面還沒被真正渲染前,就先對請求進行攔截與處理的機制。因此,Middleware 常被用來執行重新導向(Redirect)、修改 request 資料(如 URL 重寫、加上 headers) 等操作。

Page Router 和 App Router 上 Middleware 的差異

大概了解 Middlearware 以及 Middleware 在 Next.js 中是什麼樣的角色後,也來進一步看一下在 Page Router 上,以及 App Router 上的 Middleware 有什麼差異?

關於使用方法的部分,在 Page Router 和 App Router 上並沒有差異。就如同前面所提到過的內容,位於 Middleware 的邏輯會在伺服器接受使用者 request 後,進入對應頁面前執行,所以會是在 Next.js 伺服器層進化的功能,也就是說在還沒進入 Router 配置內容前就會執行 Middleware。

不論是 Page Router 還是 App Router 都是透過在專案內的 src 資料夾底下建立名為 middleware 的檔案來加入 Middleware 的內容。
https://ithelp.ithome.com.tw/upload/images/20250917/20130914OMOptVEgVf.png

middleware 檔案內的內容大概會是以下這個樣子。

import { NextResponse, NextRequest } from 'next/server'
 
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url))
}
 
export const config = {
  matcher: '/about',
}

雖然在 Next.js 的 Page Router 或是 App Router 中可以在 Server 端上在進入頁面之前,就做權限的判斷決定要不要繼續進入該頁面,但如果想要在發送 Request 後,進入 Router 設定之前,就做權限的判斷,則可以考慮改成透過 Middleware 來處理。

用 MiddleWare 處理頁面的權限控管

緊接著我們就直接來看看要怎麼透過 Middleware 來處理頁面的權限控管吧!

不論是在 Page Router 或是 App Router 都可以透過以下的這個寫法去做頁面權限控管的處理。

就如同前面所說的一樣,先建立一個 middleware 檔案在 src 的資料夾內。
然後在於 middleware 檔案內透過 config 來定義 matcher,以及透過 NextResponse 來處理當對應到特定網址後,進一步依照有沒有 token 後,做相對應的處理。

import { NextResponse, NextRequest } from "next/server";

// 定義哪些路徑要被 Middleware 處理
export const config = {
  matcher: ['/about'], // about 頁面要被 middleware 處理
};

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')?.value;

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  // 有 token 就繼續往下進行
  return NextResponse.next();
}

這樣寫的話,即使 Router 中沒有做任何權限判斷的處理,也能處理可不可以進入頁面的權限處理。

用 Middleware 和 App Router Layout 的處理權限控管的差異

我們已經知道不論 Page Router 還是 App Router 都可以透過 Middleware 來處理頁面權限控管,那我們再進一步思考一下在 App Router 中用 Next.js 提供的 layout 功能就能很方便地處理權限控制了,為什麼還需要使用 Middleware 呢?它們之間的差異是什麼?

在正式談兩者的差異之前,先來回顧一下在 App Router 中透過 layout 去做頁面權限的處理,和使用 Middleware 處理頁面權限處理時,實際上經歷了哪些流程。

- App Router 的 layout
使用者發送 HTTP Request → Next.js 根據 request 的 URL 找到 Router 設定內容對應的路由片段 (Route Segment) → 執行此路由片段 (Route Segment) 的 layout 檔案 → 透過 layout 檔案判斷是否有權限 → 有權限:渲染對應的頁面;無權限:導轉到特定頁面

- Middleware
使用者發送 HTTP Request → 進入 middlware 檔案 → 在 middleware 檔案內根據 URL 及 cookies 斷進入頁面的權限 → 有權限:呼叫 NextResponse.next() 繼續進入對應的 Route;無權限:NextResponse.redirect() 導向指定頁面 → 根據要進入到哪個頁面的結果,找到對應的路由片段 (Route Segment) → 執入對應路由片段 (Route Segment) 的 layout 檔案 → 渲染對應的頁面

重新回顧上述兩者渲染出頁面的過程,其實就可以感受到雖然達到的目的相同,但卻有差異存在,兩者之間的差異主要就是在於做權限判斷的時機點不同

使用 App Router 的 layout 進行頁面的權限判斷時,會先經過 Router 設定、匹配到對應的路由片段 (Route Segment),才會進入到透過 layout 的邏輯進行權限控制的部分。因此即使最後被導向到其他頁面,還是會先觸發一部分的頁面初始化(如果在某些 layout 內容較複雜的情況下,可能會影響效能);而透過 Middleware 進行權限判斷,則是會在還沒匹配到對應的路由片段 (Route Segment) 之前就進行判斷,也就可以避免掉不必要的頁面初始化及渲染。

雖然從流程來看,透過 Middleware 來處理權限控管似乎比透過 layout 還更合適且更有效率,因為它可以在進入頁面之前就完成驗證,避免不必要的渲染與資源浪費。但是在 Middleware 階段做處理的話,因為沒辦法接收 props,所以如果判斷邏輯很複雜,或是需要一些動態資料進行判斷,並不是只使用 token 下去做判斷,就不適合使用 Middleware 來處理。


今天 Middleware 的主題就到這裡告一個段落了!這裡必須要補充一個部分,雖然我們前面提到的都是用 Middleware 來處理頁面的權限控制,但是 Middleware 除了可以用來做頁面的權限控管外,還可以應用於各種「請求到達伺服器後、進入頁面前」的需求。像是 URL 的 Rewrite 或 Redirect、在 request 上加上特定的 Header 或是 Cookie 等。

總結來說,Middleware 適合用於「不依賴 React、只靠請求資訊就能判斷的邏輯」,如果是和畫面渲染,甚至是和元件狀態有關的邏輯,就不適合使用 Middleware 來處理。

參考資料

官方文件 - Middleware


上一篇
【Day 20】React vs Next.js 實作頁面導向的權限控制
系列文
從 React 學 Next.js:不只要會用,還要真的懂21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言